From dcd6c28312cdcb6453f32dede27b72cbd8cef48d Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Sun, 19 Sep 2021 22:36:34 -0700 Subject: [PATCH 01/48] add thiscall abi --- src/ffi.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ffi.md b/src/ffi.md index 825be5a..1c4cbc6 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -516,6 +516,7 @@ are: * `aapcs` * `cdecl` * `fastcall` +* `thiscall` * `vectorcall` This is currently hidden behind the `abi_vectorcall` gate and is subject to change. * `Rust` From f37e0b25f0c6ea13d36e28f89e68f91bed96eb5a Mon Sep 17 00:00:00 2001 From: zhenya-1007 <102388922+zhenya-1007@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:50:35 -0700 Subject: [PATCH 02/48] Update lifetimes.md Get rid of the obviously wrong (and potentially confusing) double lifetime annotation. My guess as to the etiology is an errant copy-and-paste. --- src/lifetimes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lifetimes.md b/src/lifetimes.md index ef86b7b..67595a0 100644 --- a/src/lifetimes.md +++ b/src/lifetimes.md @@ -55,7 +55,7 @@ likely desugar to the following: let y: &'b i32 = &'b x; 'c: { // ditto on 'c - let z: &'c &'b i32 = &'c y; + let z: &'c i32 = &'c y; } } } From 6018eccdf60dddc0a7753a8ace53431d5a5f5725 Mon Sep 17 00:00:00 2001 From: zhenya-1007 <102388922+zhenya-1007@users.noreply.github.com> Date: Thu, 30 Jun 2022 07:33:09 -0700 Subject: [PATCH 03/48] Update lifetimes.md Add a comment that would have made the reading smoother for me, at least. --- src/lifetimes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lifetimes.md b/src/lifetimes.md index 67595a0..f55ea8c 100644 --- a/src/lifetimes.md +++ b/src/lifetimes.md @@ -55,7 +55,7 @@ likely desugar to the following: let y: &'b i32 = &'b x; 'c: { // ditto on 'c - let z: &'c i32 = &'c y; + let z: &'c &'b i32 = &'c y; // "a reference to a reference to an i32" (with lifetimes annotated) } } } From 341005b55917e8d137cb9a8b08554d5977b8b231 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 27 Jul 2022 20:51:00 +0900 Subject: [PATCH 04/48] Update the `repr(transparent)` section to reflect the current state Signed-off-by: Yuki Okushi --- src/other-reprs.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/other-reprs.md b/src/other-reprs.md index 93da729..c17f0ac 100644 --- a/src/other-reprs.md +++ b/src/other-reprs.md @@ -56,24 +56,27 @@ compiled as normal.) ## repr(transparent) -This can only be used on structs with a single non-zero-sized field (there may -be additional zero-sized fields). The effect is that the layout and ABI of the -whole struct is guaranteed to be the same as that one field. +This can only be used on structs with a single non-zero-sized field +or enums with only one non-zero-sized field (there may be additional zero-sized fields). +The effect is that the layout and ABI of the whole struct/enum is guaranteed to be the same as that one field. + +> NOTE: There's a `transparent_unions` nightly feature to apply `repr(transparent)` to unions, +> but it hasn't been stabilized due to design concerns. See the [tracking issue][issue-60405] for more details. The goal is to make it possible to transmute between the single field and the -struct. An example of that is [`UnsafeCell`], which can be transmuted into +struct/enum. An example of that is [`UnsafeCell`], which can be transmuted into the type it wraps ([`UnsafeCell`] also uses the unstable [no_niche][no-niche-pull], so its ABI is not actually guaranteed to be the same when nested in other types). -Also, passing the struct through FFI where the inner field type is expected on -the other side is guaranteed to work. In particular, this is necessary for `struct -Foo(f32)` to always have the same ABI as `f32`. +Also, passing the struct/enum through FFI where the inner field type is expected on +the other side is guaranteed to work. In particular, this is necessary for +`struct Foo(f32)` or `enum Foo { Bar(f32) }` to always have the same ABI as `f32`. This repr is only considered part of the public ABI of a type if either the single field is `pub`, or if its layout is documented in prose. Otherwise, the layout should not be relied upon by other crates. -More details are in the [RFC][rfc-transparent]. +More details are in the [RFC 1758][rfc-transparent] and the [RFC 2645][rfc-transparent-unions-enums]. ## repr(u*), repr(i*) @@ -153,8 +156,10 @@ This is a modifier on `repr(C)` and `repr(Rust)`. It is incompatible with [unsafe code guidelines]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html [drop flags]: drop-flags.html [ub loads]: https://github.com/rust-lang/rust/issues/27060 +[issue-60405]: https://github.com/rust-lang/rust/issues/60405 [`UnsafeCell`]: ../std/cell/struct.UnsafeCell.html [rfc-transparent]: https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md +[rfc-transparent-unions-enums]: https://rust-lang.github.io/rfcs/2645-transparent-unions.html [really-tagged]: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md [rust-bindgen]: https://rust-lang.github.io/rust-bindgen/ [cbindgen]: https://github.com/eqrion/cbindgen From 37be7eae0481b393371ff32d88bdb6dc0c046ca5 Mon Sep 17 00:00:00 2001 From: Edoardo Costantini Date: Wed, 3 Aug 2022 16:23:15 +0200 Subject: [PATCH 05/48] [fix] typo --- src/lifetime-mismatch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lifetime-mismatch.md b/src/lifetime-mismatch.md index 0494d49..1da2d28 100644 --- a/src/lifetime-mismatch.md +++ b/src/lifetime-mismatch.md @@ -76,7 +76,7 @@ care about, but the lifetime system is too coarse-grained to handle that. The following code fails to compile, because Rust sees that a variable, `map`, is borrowed twice, and can not infer that the first borrow stops to be needed before the second one occurs. This is caused by Rust conservatively falling back -to using a whole scope for the first borow. This will eventually get fixed. +to using a whole scope for the first borrow. This will eventually get fixed. ```rust,compile_fail # use std::collections::HashMap; From 7df62756a17fe5605ab437fd27ba8b69bb885e59 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 16 Aug 2022 07:01:41 +0900 Subject: [PATCH 06/48] Apply a suggestion Co-authored-by: Eric Huss --- src/other-reprs.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/other-reprs.md b/src/other-reprs.md index c17f0ac..228b22b 100644 --- a/src/other-reprs.md +++ b/src/other-reprs.md @@ -56,8 +56,7 @@ compiled as normal.) ## repr(transparent) -This can only be used on structs with a single non-zero-sized field -or enums with only one non-zero-sized field (there may be additional zero-sized fields). +`#[repr(transparent)]` can only be used on a struct or single-variant enum that has a single non-zero-sized field (there may be additional zero-sized fields). The effect is that the layout and ABI of the whole struct/enum is guaranteed to be the same as that one field. > NOTE: There's a `transparent_unions` nightly feature to apply `repr(transparent)` to unions, From 276874169cf84788cb0dd33476b53e1c1967c686 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 5 Sep 2022 16:07:53 +0200 Subject: [PATCH 07/48] Small typo --- src/transmutes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transmutes.md b/src/transmutes.md index 1f6d7d5..eea8668 100644 --- a/src/transmutes.md +++ b/src/transmutes.md @@ -2,7 +2,7 @@ Get out of our way type system! We're going to reinterpret these bits or die trying! Even though this book is all about doing things that are unsafe, I -really can't emphasize that you should deeply think about finding Another Way +really can't emphasize enough that you should deeply think about finding Another Way than the operations covered in this section. This is really, truly, the most horribly unsafe thing you can do in Rust. The guardrails here are dental floss. From 9c73283775466d22208a0b28afcab44db4c0cc10 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Thu, 29 Sep 2022 15:31:22 -0700 Subject: [PATCH 08/48] Fix typo (#380) --- src/subtyping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subtyping.md b/src/subtyping.md index 91e870e..6f0c12d 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -252,7 +252,7 @@ type of the referent is shared knowledge, which is why adjusting that type in on one place (the reference) can lead to issues. But if you shrink down a reference's lifetime when you hand it to someone, that lifetime information isn't shared in any way. There are now two independent references with independent lifetimes. -There's no way to mess with original reference's lifetime using the other one. +There's no way to mess with the original reference's lifetime using the other one. Or rather, the only way to mess with someone's lifetime is to build a meowing dog. But as soon as you try to build a meowing dog, the lifetime should be wrapped up From 05532356e7a4dbea2330aabb77611f5179493bb8 Mon Sep 17 00:00:00 2001 From: Redglyph <31706195+blueglyph@users.noreply.github.com> Date: Sat, 5 Nov 2022 00:13:14 +0100 Subject: [PATCH 09/48] Fix typos and grammatical errors (#384) Co-authored-by: redglyph --- src/unchecked-uninit.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/unchecked-uninit.md b/src/unchecked-uninit.md index c61415c..b3dd31c 100644 --- a/src/unchecked-uninit.md +++ b/src/unchecked-uninit.md @@ -11,7 +11,7 @@ Unsafe Rust gives us a powerful tool to handle this problem: [`MaybeUninit`]. This type can be used to handle memory that has not been fully initialized yet. -With `MaybeUninit`, we can initialize an array element-for-element as follows: +With `MaybeUninit`, we can initialize an array element by element as follows: ```rust use std::mem::{self, MaybeUninit}; @@ -79,8 +79,7 @@ This code proceeds in three steps: acknowledge that by providing appropriate methods). It's worth spending a bit more time on the loop in the middle, and in particular -the assignment operator and its interaction with `drop`. If we would have -written something like: +the assignment operator and its interaction with `drop`. If we wrote something like: ```rust,ignore @@ -88,7 +87,7 @@ written something like: ``` we would actually overwrite a `Box`, leading to `drop` of uninitialized -data, which will cause much sadness and pain. +data, which would cause much sadness and pain. The correct alternative, if for some reason we cannot use `MaybeUninit::new`, is to use the [`ptr`] module. In particular, it provides three functions that allow @@ -97,7 +96,7 @@ us to assign bytes to a location in memory without dropping the old value: * `ptr::write(ptr, val)` takes a `val` and moves it into the address pointed to by `ptr`. -* `ptr::copy(src, dest, count)` copies the bits that `count` T's would occupy +* `ptr::copy(src, dest, count)` copies the bits that `count` T items would occupy from src to dest. (this is equivalent to C's memmove -- note that the argument order is reversed!) * `ptr::copy_nonoverlapping(src, dest, count)` does what `copy` does, but a @@ -105,8 +104,8 @@ us to assign bytes to a location in memory without dropping the old value: (this is equivalent to C's memcpy -- note that the argument order is reversed!) It should go without saying that these functions, if misused, will cause serious -havoc or just straight up Undefined Behavior. The only things that these -functions *themselves* require is that the locations you want to read and write +havoc or just straight up Undefined Behavior. The only requirement of these +functions *themselves* is that the locations you want to read and write are allocated and properly aligned. However, the ways writing arbitrary bits to arbitrary locations of memory can break things are basically uncountable! From 6570607bccb89549bfab6698cb6e9889d087e9e9 Mon Sep 17 00:00:00 2001 From: James Williams Date: Mon, 14 Nov 2022 17:05:20 +0000 Subject: [PATCH 10/48] Grammar change for lifetime-mismatch.md "[...] stops to be needed [...]" -> "[...] ceases to be needed [...]" --- src/lifetime-mismatch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lifetime-mismatch.md b/src/lifetime-mismatch.md index 1da2d28..bc53f06 100644 --- a/src/lifetime-mismatch.md +++ b/src/lifetime-mismatch.md @@ -74,7 +74,7 @@ care about, but the lifetime system is too coarse-grained to handle that. ## Improperly reduced borrows The following code fails to compile, because Rust sees that a variable, `map`, -is borrowed twice, and can not infer that the first borrow stops to be needed +is borrowed twice, and can not infer that the first borrow ceases to be needed before the second one occurs. This is caused by Rust conservatively falling back to using a whole scope for the first borrow. This will eventually get fixed. From ae406aa5287a9e025abb72343aaceec98458c117 Mon Sep 17 00:00:00 2001 From: nils <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 21 Nov 2022 23:48:20 +0100 Subject: [PATCH 11/48] Improve chapter about `Vec` (#381) Co-authored-by: Daniel Henry-Mantilla --- src/vec/vec-alloc.md | 1 - src/vec/vec-final.md | 2 -- src/vec/vec-insert-remove.md | 16 ++++++++++------ src/vec/vec-into-iter.md | 14 ++++++-------- src/vec/vec-layout.md | 19 ++++++++----------- src/vec/vec-raw.md | 2 -- src/vec/vec-zsts.md | 5 ++--- 7 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/vec/vec-alloc.md b/src/vec/vec-alloc.md index 6c69c93..2495473 100644 --- a/src/vec/vec-alloc.md +++ b/src/vec/vec-alloc.md @@ -28,7 +28,6 @@ impl Vec { ptr: NonNull::dangling(), len: 0, cap: 0, - _marker: PhantomData, } } } diff --git a/src/vec/vec-final.md b/src/vec/vec-final.md index 1f8af37..696391d 100644 --- a/src/vec/vec-final.md +++ b/src/vec/vec-final.md @@ -10,7 +10,6 @@ use std::ptr::{self, NonNull}; struct RawVec { ptr: NonNull, cap: usize, - _marker: PhantomData, } unsafe impl Send for RawVec {} @@ -25,7 +24,6 @@ impl RawVec { RawVec { ptr: NonNull::dangling(), cap: cap, - _marker: PhantomData, } } diff --git a/src/vec/vec-insert-remove.md b/src/vec/vec-insert-remove.md index 57283f9..722e20c 100644 --- a/src/vec/vec-insert-remove.md +++ b/src/vec/vec-insert-remove.md @@ -22,9 +22,11 @@ pub fn insert(&mut self, index: usize, elem: T) { unsafe { // ptr::copy(src, dest, len): "copy from src to dest len elems" - ptr::copy(self.ptr.as_ptr().add(index), - self.ptr.as_ptr().add(index + 1), - self.len - index); + ptr::copy( + self.ptr.as_ptr().add(index), + self.ptr.as_ptr().add(index + 1), + self.len - index, + ); ptr::write(self.ptr.as_ptr().add(index), elem); self.len += 1; } @@ -42,9 +44,11 @@ pub fn remove(&mut self, index: usize) -> T { unsafe { self.len -= 1; let result = ptr::read(self.ptr.as_ptr().add(index)); - ptr::copy(self.ptr.as_ptr().add(index + 1), - self.ptr.as_ptr().add(index), - self.len - index); + ptr::copy( + self.ptr.as_ptr().add(index + 1), + self.ptr.as_ptr().add(index), + self.len - index, + ); result } } diff --git a/src/vec/vec-into-iter.md b/src/vec/vec-into-iter.md index 61782e3..a3a3c8c 100644 --- a/src/vec/vec-into-iter.md +++ b/src/vec/vec-into-iter.md @@ -49,7 +49,6 @@ pub struct IntoIter { cap: usize, start: *const T, end: *const T, - _marker: PhantomData, } ``` @@ -61,13 +60,13 @@ impl IntoIterator for Vec { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - // Can't destructure Vec since it's Drop - let ptr = self.ptr; - let cap = self.cap; - let len = self.len; - // Make sure not to drop Vec since that would free the buffer - mem::forget(self); + let vec = ManuallyDrop::new(self); + + // Can't destructure Vec since it's Drop + let ptr = vec.ptr; + let cap = vec.cap; + let len = vec.len; unsafe { IntoIter { @@ -80,7 +79,6 @@ impl IntoIterator for Vec { } else { ptr.as_ptr().add(len) }, - _marker: PhantomData, } } } diff --git a/src/vec/vec-layout.md b/src/vec/vec-layout.md index c1c1afc..9129952 100644 --- a/src/vec/vec-layout.md +++ b/src/vec/vec-layout.md @@ -15,13 +15,10 @@ pub struct Vec { } ``` -And indeed this would compile. Unfortunately, it would be incorrect. First, the +And indeed this would compile. Unfortunately, it would be too strict. The compiler will give us too strict variance. So a `&Vec<&'static str>` -couldn't be used where an `&Vec<&'a str>` was expected. More importantly, it -will give incorrect ownership information to the drop checker, as it will -conservatively assume we don't own any values of type `T`. See [the chapter -on ownership and lifetimes][ownership] for all the details on variance and -drop check. +couldn't be used where a `&Vec<&'a str>` was expected. See [the chapter +on ownership and lifetimes][ownership] for all the details on variance. As we saw in the ownership chapter, the standard library uses `Unique` in place of `*mut T` when it has a raw pointer to an allocation that it owns. Unique is unstable, @@ -30,16 +27,16 @@ so we'd like to not use it if possible, though. As a recap, Unique is a wrapper around a raw pointer that declares that: * We are covariant over `T` -* We may own a value of type `T` (for drop check) +* We may own a value of type `T` (this is not relevant for our example here, but see + [the chapter on PhantomData][phantom-data] on why the real `std::vec::Vec` needs this) * We are Send/Sync if `T` is Send/Sync * Our pointer is never null (so `Option>` is null-pointer-optimized) We can implement all of the above requirements in stable Rust. To do this, instead of using `Unique` we will use [`NonNull`][NonNull], another wrapper around a raw pointer, which gives us two of the above properties, namely it is covariant -over `T` and is declared to never be null. By adding a `PhantomData` (for drop -check) and implementing Send/Sync if `T` is, we get the same results as using -`Unique`: +over `T` and is declared to never be null. By implementing Send/Sync if `T` is, +we get the same results as using `Unique`: ```rust use std::ptr::NonNull; @@ -49,7 +46,6 @@ pub struct Vec { ptr: NonNull, cap: usize, len: usize, - _marker: PhantomData, } unsafe impl Send for Vec {} @@ -58,4 +54,5 @@ unsafe impl Sync for Vec {} ``` [ownership]: ../ownership.html +[phantom-data]: ../phantom-data.md [NonNull]: ../../std/ptr/struct.NonNull.html diff --git a/src/vec/vec-raw.md b/src/vec/vec-raw.md index e86537b..728feaa 100644 --- a/src/vec/vec-raw.md +++ b/src/vec/vec-raw.md @@ -13,7 +13,6 @@ allocating, growing, and freeing: struct RawVec { ptr: NonNull, cap: usize, - _marker: PhantomData, } unsafe impl Send for RawVec {} @@ -25,7 +24,6 @@ impl RawVec { RawVec { ptr: NonNull::dangling(), cap: 0, - _marker: PhantomData, } } diff --git a/src/vec/vec-zsts.md b/src/vec/vec-zsts.md index 73a97ba..8f25297 100644 --- a/src/vec/vec-zsts.md +++ b/src/vec/vec-zsts.md @@ -33,14 +33,13 @@ method of `RawVec`. ```rust,ignore impl RawVec { fn new() -> Self { - // !0 is usize::MAX. This branch should be stripped at compile time. - let cap = if mem::size_of::() == 0 { !0 } else { 0 }; + // This branch should be stripped at compile time. + let cap = if mem::size_of::() == 0 { usize::MAX } else { 0 }; // `NonNull::dangling()` doubles as "unallocated" and "zero-sized allocation" RawVec { ptr: NonNull::dangling(), cap: cap, - _marker: PhantomData, } } From dd37e21ccee43918ed18a71581bb2af537ffe4fc Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 9 Dec 2022 23:04:38 +0100 Subject: [PATCH 12/48] Update lifetime-mismatch.md (#387) Fix sentence grammar --- src/lifetime-mismatch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lifetime-mismatch.md b/src/lifetime-mismatch.md index bc53f06..ecb6cf2 100644 --- a/src/lifetime-mismatch.md +++ b/src/lifetime-mismatch.md @@ -65,7 +65,7 @@ fn main() { The lifetime system is forced to extend the `&mut foo` to have lifetime `'c`, due to the lifetime of `loan` and `mutate_and_share`'s signature. Then when we -try to call `share`, and it sees we're trying to alias that `&'c mut foo` and +try to call `share`, it sees we're trying to alias that `&'c mut foo` and blows up in our face! This program is clearly correct according to the reference semantics we actually From 734ca5e3f7baf356c3e01ad9e6032317d85772de Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sat, 17 Dec 2022 14:26:28 +0300 Subject: [PATCH 13/48] Don't try to work with invalid values on non-null types, always do what nomicon says --- src/ffi.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ffi.md b/src/ffi.md index 9b0ff98..8d1a882 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -659,7 +659,8 @@ Certain Rust types are defined to never be `null`. This includes references (`&T `&mut T`), boxes (`Box`), and function pointers (`extern "abi" fn()`). When interfacing with C, pointers that might be `null` are often used, which would seem to require some messy `transmute`s and/or unsafe code to handle conversions to/from Rust types. -However, the language provides a workaround. +However, trying to construct/work with these invalid values **is undefined behavior**, +so you should use the following workaround instead. As a special case, an `enum` is eligible for the "nullable pointer optimization" if it contains exactly two variants, one of which contains no data and the other contains a field of one of the From 03fa5be4590abc32b8294f105c3e3a873ab16b50 Mon Sep 17 00:00:00 2001 From: "Hugo E.-Lahsen - xy2_" Date: Thu, 5 Jan 2023 11:20:31 +0100 Subject: [PATCH 14/48] borrow-splitting: Use `take` instead of `replace` (#391) --- src/borrow-splitting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borrow-splitting.md b/src/borrow-splitting.md index 3d13ff9..08b6d0d 100644 --- a/src/borrow-splitting.md +++ b/src/borrow-splitting.md @@ -159,7 +159,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option { - let slice = mem::replace(&mut self.0, &mut []); + let slice = mem::take(&mut self.0); if slice.is_empty() { return None; } let (l, r) = slice.split_at_mut(1); @@ -170,7 +170,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { fn next_back(&mut self) -> Option { - let slice = mem::replace(&mut self.0, &mut []); + let slice = mem::take(&mut self.0); if slice.is_empty() { return None; } let new_len = slice.len() - 1; From 960d610e7f33889a2577f5f17c26f0d5c82b30df Mon Sep 17 00:00:00 2001 From: nicoo Date: Fri, 6 Jan 2023 12:51:41 +0100 Subject: [PATCH 15/48] vec/raw: Simplify `RawVec::grow` (#392) --- src/vec/vec-raw.md | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/vec/vec-raw.md b/src/vec/vec-raw.md index 728feaa..0bca2da 100644 --- a/src/vec/vec-raw.md +++ b/src/vec/vec-raw.md @@ -28,18 +28,13 @@ impl RawVec { } fn grow(&mut self) { - let (new_cap, new_layout) = if self.cap == 0 { - (1, Layout::array::(1).unwrap()) - } else { - // This can't overflow because we ensure self.cap <= isize::MAX. - let new_cap = 2 * self.cap; - - // Layout::array checks that the number of bytes is <= usize::MAX, - // but this is redundant since old_layout.size() <= isize::MAX, - // so the `unwrap` should never fail. - let new_layout = Layout::array::(new_cap).unwrap(); - (new_cap, new_layout) - }; + // This can't overflow because we ensure self.cap <= isize::MAX. + let new_cap = if self.cap == 0 { 1 } else { 2 * self.cap }; + + // Layout::array checks that the number of bytes is <= usize::MAX, + // but this is redundant since old_layout.size() <= isize::MAX, + // so the `unwrap` should never fail. + let new_layout = Layout::array::(new_cap).unwrap(); // Ensure that the new allocation doesn't exceed `isize::MAX` bytes. assert!(new_layout.size() <= isize::MAX as usize, "Allocation too large"); From 94bb6a629bd66af48c5c9cd20406f27aab0334e7 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 13 Jan 2023 21:07:10 -0500 Subject: [PATCH 16/48] Copyedits to FFI and unwinding section These are minor changes that make the text flow better to me. --- src/ffi.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ffi.md b/src/ffi.md index 8d1a882..853009b 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -722,16 +722,19 @@ No `transmute` required! ## FFI and unwinding It’s important to be mindful of unwinding when working with FFI. Each -non-`Rust` ABI comes in two variants, one with `-unwind` suffix and one without. If -you expect Rust `panic`s or foreign (e.g. C++) exceptions to cross an FFI -boundary, that boundary must use the appropriate `-unwind` ABI string (note -that compiling with `panic=abort` will still cause `panic!` to immediately -abort the process, regardless of which ABI is specified by the function that -`panic`s). +ABI comes in two variants, one with `-unwind` suffix and one without (except +the `Rust` ABI, which always permits unwinding). +If you expect Rust `panic`s or foreign (e.g. C++) exceptions to cross an FFI +boundary, that boundary must use the appropriate `-unwind` ABI string. Conversely, if you do not expect unwinding to cross an ABI boundary, use one of -the non-`unwind` ABI strings (other than `Rust`, which always permits -unwinding). If an unwinding operation does encounter an ABI boundary that is +the non-`unwind` ABI strings. + +> Note: Compiling with `panic=abort` will still cause `panic!` to immediately +abort the process, regardless of which ABI is specified by the function that +`panic`s. + +If an unwinding operation does encounter an ABI boundary that is not permitted to unwind, the behavior depends on the source of the unwinding (Rust `panic` or a foreign exception): From d1a59f543e6f7a5469b4a15d8122b0cdd05434a0 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Mon, 16 Jan 2023 19:31:31 -0700 Subject: [PATCH 17/48] Re-word: only "most" ABIs have `-unwind` --- src/ffi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ffi.md b/src/ffi.md index 853009b..684e712 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -721,9 +721,9 @@ No `transmute` required! ## FFI and unwinding -It’s important to be mindful of unwinding when working with FFI. Each -ABI comes in two variants, one with `-unwind` suffix and one without (except -the `Rust` ABI, which always permits unwinding). +It’s important to be mindful of unwinding when working with FFI. Most +ABI strings come in two variants, one with an `-unwind` suffix and one without. +The `Rust` ABI always permits unwinding, so there is no `Rust-unwind` ABI. If you expect Rust `panic`s or foreign (e.g. C++) exceptions to cross an FFI boundary, that boundary must use the appropriate `-unwind` ABI string. From 641e541b93ac533c1402323b067e6e0105e5e659 Mon Sep 17 00:00:00 2001 From: Alex Rao Date: Tue, 17 Jan 2023 13:22:32 -0500 Subject: [PATCH 18/48] Fix typo in dropck documentation This fixes `gatget` to say `gadget` --- src/dropck.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dropck.md b/src/dropck.md index 7594021..4063d56 100644 --- a/src/dropck.md +++ b/src/dropck.md @@ -250,7 +250,7 @@ fn main() { inspector: None, days: Box::new(1), }; - world.inspector = Some(Inspector(&world.days, "gatget")); + world.inspector = Some(Inspector(&world.days, "gadget")); } ``` From 2d3e6f67d5293651180f977ccef60ef7778ae952 Mon Sep 17 00:00:00 2001 From: Martin <1544429+MPvHarmelen@users.noreply.github.com> Date: Tue, 7 Feb 2023 15:28:03 +0545 Subject: [PATCH 19/48] Small language fix in subtyping.md (#399) --- src/subtyping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subtyping.md b/src/subtyping.md index 6f0c12d..cc48a59 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -339,7 +339,7 @@ lifetimes to be covariant: as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad. -However Box makes it easier to focus on by-value aspect of references that we +However, Box makes it easier to focus on the by-value aspect of references that we partially glossed over. Unlike a lot of languages which allow values to be freely aliased at all times, From 79b53665a7c61d171fb8c5ad0b73b371f9ee6ba7 Mon Sep 17 00:00:00 2001 From: Dmitry Mottl Date: Mon, 13 Feb 2023 10:40:24 +0200 Subject: [PATCH 20/48] Fixes double bug in Send-Sync example (#401) --- src/send-and-sync.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/send-and-sync.md b/src/send-and-sync.md index 34539da..808a5c3 100644 --- a/src/send-and-sync.md +++ b/src/send-and-sync.md @@ -94,6 +94,7 @@ to the heap. use std::{ mem::{align_of, size_of}, ptr, + cmp::max, }; struct Carton(ptr::NonNull); @@ -105,8 +106,8 @@ impl Carton { let mut memptr: *mut T = ptr::null_mut(); unsafe { let ret = libc::posix_memalign( - (&mut memptr).cast(), - align_of::(), + (&mut memptr as *mut *mut T).cast(), + max(align_of::(), size_of::()), size_of::() ); assert_eq!(ret, 0, "Failed to allocate or invalid alignment"); From 51bb051414bcc8ae47108de33288980290cda821 Mon Sep 17 00:00:00 2001 From: Matt Harding Date: Sun, 12 Mar 2023 20:35:56 +0000 Subject: [PATCH 21/48] Change incorrect reference to "above" in ffi.md --- src/ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ffi.md b/src/ffi.md index 684e712..55be225 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -258,7 +258,7 @@ pub extern "C" fn hello_from_rust() { # fn main() {} ``` -The `extern "C"` makes this function adhere to the C calling convention, as discussed above in "[Foreign Calling Conventions]". +The `extern "C"` makes this function adhere to the C calling convention, as discussed below in "[Foreign Calling Conventions]". The `no_mangle` attribute turns off Rust's name mangling, so that it has a well defined symbol to link to. Then, to compile Rust code as a shared library that can be called from C, add the following to your `Cargo.toml`: From b5f7500fc40775096c2bbd204eae096612cf9047 Mon Sep 17 00:00:00 2001 From: masloyet <59209784+masloyet@users.noreply.github.com> Date: Mon, 27 Mar 2023 09:47:36 -0400 Subject: [PATCH 22/48] Fix typo in 3.8 Subtyping and Variance (#395) Co-authored-by: Mason Loyet --- src/subtyping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subtyping.md b/src/subtyping.md index cc48a59..79b29be 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -335,7 +335,7 @@ we inherited invariance as soon as we put our reference inside an `&mut T`. As it turns out, the argument for why it's ok for Box (and Vec, Hashmap, etc.) to be covariant is pretty similar to the argument for why it's ok for -lifetimes to be covariant: as soon as you try to stuff them in something like a +references to be covariant: as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad. From b91834421241386b40f5e05e2510589653193fe8 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 9 May 2023 06:51:24 +0200 Subject: [PATCH 23/48] Expand unbounded lifetime example code and improve wording --- src/unbounded-lifetimes.md | 39 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/unbounded-lifetimes.md b/src/unbounded-lifetimes.md index 03febd6..89dcc60 100644 --- a/src/unbounded-lifetimes.md +++ b/src/unbounded-lifetimes.md @@ -1,13 +1,13 @@ # Unbounded Lifetimes Unsafe code can often end up producing references or lifetimes out of thin air. -Such lifetimes come into the world as *unbounded*. The most common source of this -is dereferencing a raw pointer, which produces a reference with an unbounded lifetime. -Such a lifetime becomes as big as context demands. This is in fact more powerful -than simply becoming `'static`, because for instance `&'static &'a T` -will fail to typecheck, but the unbound lifetime will perfectly mold into -`&'a &'a T` as needed. However for most intents and purposes, such an unbounded -lifetime can be regarded as `'static`. +Such lifetimes come into the world as *unbounded*. The most common source of +this is taking a reference to a dereferenced raw pointer, which produces a +reference with an unbounded lifetime. Such a lifetime becomes as big as context +demands. This is in fact more powerful than simply becoming `'static`, because +for instance `&'static &'a T` will fail to typecheck, but the unbound lifetime +will perfectly mold into `&'a &'a T` as needed. However for most intents and +purposes, such an unbounded lifetime can be regarded as `'static`. Almost no reference is `'static`, so this is probably wrong. `transmute` and `transmute_copy` are the two other primary offenders. One should endeavor to @@ -17,17 +17,24 @@ boundaries. Given a function, any output lifetimes that don't derive from inputs are unbounded. For instance: - -```rust,ignore -fn get_str<'a>() -> &'a str; +```rust +fn get_str<'a>(s: *const String) -> &'a str { + unsafe { &*s } +} + +fn main() { + let soon_dropped = String::from("hello"); + let dangling = get_str(&soon_dropped); + drop(soon_dropped); + println!("Invalid str: {}", dangling); // Invalid str: gӚ_` +} ``` -will produce an `&str` with an unbounded lifetime. The easiest way to avoid -unbounded lifetimes is to use lifetime elision at the function boundary. -If an output lifetime is elided, then it *must* be bounded by an input lifetime. -Of course it might be bounded by the *wrong* lifetime, but this will usually -just cause a compiler error, rather than allow memory safety to be trivially -violated. +The easiest way to avoid unbounded lifetimes is to use lifetime elision at the +function boundary. If an output lifetime is elided, then it *must* be bounded by +an input lifetime. Of course it might be bounded by the *wrong* lifetime, but +this will usually just cause a compiler error, rather than allow memory safety +to be trivially violated. Within a function, bounding lifetimes is more error-prone. The safest and easiest way to bound a lifetime is to return it from a function with a bound lifetime. From c6e4a51d59b761dd3b3f79436485613e630856e6 Mon Sep 17 00:00:00 2001 From: LucasFA <23667494+LucasFA@users.noreply.github.com> Date: Sun, 14 May 2023 20:43:58 +0200 Subject: [PATCH 24/48] Update to 2021 edition --- book.toml | 2 +- src/intro.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book.toml b/book.toml index a2011c6..693aca4 100644 --- a/book.toml +++ b/book.toml @@ -32,4 +32,4 @@ git-repository-url = "https://github.com/rust-lang/nomicon" "./arc.html" = "./arc-mutex/arc.html" [rust] -edition = "2018" +edition = "2021" diff --git a/src/intro.md b/src/intro.md index 4e77ffa..323c0ce 100644 --- a/src/intro.md +++ b/src/intro.md @@ -39,7 +39,7 @@ Topics that are within the scope of this book include: the meaning of (un)safety The Rustonomicon is not a place to exhaustively describe the semantics and guarantees of every single API in the standard library, nor is it a place to exhaustively describe every feature of Rust. -Unless otherwise noted, Rust code in this book uses the Rust 2018 edition. +Unless otherwise noted, Rust code in this book uses the Rust 2021 edition. [trpl]: ../book/index.html [ref]: ../reference/index.html From 87f56947f382d4de226a4354c255566762052c20 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 19 May 2023 11:09:09 -0700 Subject: [PATCH 25/48] Set example to no_run since it has UB We can't guarantee the behavior when running this example, so it is not safe to run during tests. --- src/unbounded-lifetimes.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/unbounded-lifetimes.md b/src/unbounded-lifetimes.md index 89dcc60..9ca2a2f 100644 --- a/src/unbounded-lifetimes.md +++ b/src/unbounded-lifetimes.md @@ -17,7 +17,8 @@ boundaries. Given a function, any output lifetimes that don't derive from inputs are unbounded. For instance: -```rust + +```rust,no_run fn get_str<'a>(s: *const String) -> &'a str { unsafe { &*s } } From ae692174244c506d03d4a4d5928081f8fcf31919 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Thu, 10 Feb 2022 09:47:54 +0000 Subject: [PATCH 26/48] begin rewriting chapter on subtyping --- src/subtyping.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/subtyping.md b/src/subtyping.md index 79b29be..8c8a180 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -1,5 +1,45 @@ # Subtyping and Variance +Rust uses lifetimes to track the relationships between borrows and ownership. +However, a naive implementation of lifetimes would be either too restrictive, +or permit undefined behaviour. Let's see a few examples: + +```rust,ignore +fn debug<'a>(a: &'a str, b: &'a str) { + println!("a = {:?} b = {:?}", a, b) +} + +fn main() { + let a: &'static str = "hello"; + { + let b = String::from("world"); + let b = &b; // 'b has a shorter lifetime than 'static + debug(a, b); + } +} +``` + +In an overly restrictive implementation of lifetimes, since `a` and `b` have differeing lifetimes, +we might see the following error: + +```text +error[E0308]: mismatched types + --> src/main.rs:6:16 + | +6 | debug(a, b); + | ^ + | | + | expected `&'static str`, found struct `&'b str` +``` + +This is over-restrictive. In this case, what we want is to accept any type that lives "at least as long" as `<'a>`. +This is what subtyping is intended to fix. + +Let's define lifetime `'a` to be a `subtype` of lifetime `'b`, if and only if `'a` lives _at least as long_ as `'b`. +We will denote this as `'a: 'b` + +--- + Subtyping is a relationship between types that allows statically typed languages to be a bit more flexible and permissive. From 9c17e30bf9a298a8d12ffb1c923aaa260a19431a Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Thu, 10 Feb 2022 13:54:55 +0000 Subject: [PATCH 27/48] continue --- src/subtyping.md | 236 +++++++++++++++++------------------------------ 1 file changed, 87 insertions(+), 149 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index 8c8a180..ce01d15 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -2,11 +2,41 @@ Rust uses lifetimes to track the relationships between borrows and ownership. However, a naive implementation of lifetimes would be either too restrictive, -or permit undefined behaviour. Let's see a few examples: +or permit undefined behavior. -```rust,ignore -fn debug<'a>(a: &'a str, b: &'a str) { - println!("a = {:?} b = {:?}", a, b) +In order to allow flexible usage of lifetimes +while also preventing mis-use, Rust uses a combination of **Subtyping** and **Variance**. + +## Subtyping + +Subtyping is the idea that one type can be a *subtype* of another. +Let's define that `A: B` is equivalent to saying '`A` is a subtype of `B`'. +What this is suggesting to us is that the set of *requirements* that `B` defines +are completely satisfied by `A`. `A` may then have more requirements. + +An example of simple subtyping that exists in the language are [supertraits](https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html?highlight=supertraits#using-supertraits-to-require-one-traits-functionality-within-another-trait) + +```rust +use std::fmt; + +trait OutlinePrint: fmt::Display { + fn outline_print(&self) { + todo!() + } +} +``` + +Here, we have that `OutlinePrint: fmt::Display` (`OutlinePrint` is a *subtype* of `Display`), +because it has all the requirements of `fmt::Display`, plus the `outline_print` function. + +However, subtyping in traits is not that interesting in the case of Rust. +Here in the nomicon, we're going to focus more with how subtyping interacts with **lifetimes** + +Take this example + +```rust +fn debug(a: T, b: T) { + println!("a = {:?} b = {:?}", a, b); } fn main() { @@ -24,163 +54,73 @@ we might see the following error: ```text error[E0308]: mismatched types - --> src/main.rs:6:16 - | -6 | debug(a, b); - | ^ - | | - | expected `&'static str`, found struct `&'b str` + --> src/main.rs:10:16 + | +10 | debug(a, b); + | ^ + | | + | expected `&'static str`, found struct `&'b str` ``` -This is over-restrictive. In this case, what we want is to accept any type that lives "at least as long" as `<'a>`. -This is what subtyping is intended to fix. - -Let's define lifetime `'a` to be a `subtype` of lifetime `'b`, if and only if `'a` lives _at least as long_ as `'b`. -We will denote this as `'a: 'b` - ---- +This is over-restrictive. In this case, what we want is to accept any type that lives *at least as long* as `'b`. +Let's try using subtyping with our lifetimes. -Subtyping is a relationship between types that allows statically typed -languages to be a bit more flexible and permissive. +Let's define a lifetime to have the a simple set of requirements: `'a` defines a region of code in which a value will be alive. +Now that we have a defined set of requirements for lifetimes, we can define how they relate to each other. +`'a: 'b` if and only if `'a` defines a region of code that **completely contains** `'b`. -Subtyping in Rust is a bit different from subtyping in other languages. This -makes it harder to give simple examples, which is a problem since subtyping, -and especially variance, is already hard to understand properly. As in, -even compiler writers mess it up all the time. +`'a` may define a region larger than `'b`, but that still fits our definition. +Going back to our example above, we can say that `'static: 'b`. -To keep things simple, this section will consider a small extension to the -Rust language that adds a new and simpler subtyping relationship. After -establishing concepts and issues under this simpler system, -we will then relate it back to how subtyping actually occurs in Rust. - -So here's our simple extension, *Objective Rust*, featuring three new types: +For now, let's accept the idea that subtypes of lifetimes can be transitive (more on this in [Variance](#variance>)), +eg. `&'static str` is a subtype of `&'b str`, then we can let them coerce, and then the example above will compile ```rust -trait Animal { - fn snuggle(&self); - fn eat(&mut self); -} - -trait Cat: Animal { - fn meow(&self); +fn debug(a: T, b: T) { + println!("a = {:?} b = {:?}", a, b); } -trait Dog: Animal { - fn bark(&self); -} -``` - -But unlike normal traits, we can use them as concrete and sized types, just like structs. - -Now, say we have a very simple function that takes an Animal, like this: - - -```rust,ignore -fn love(pet: Animal) { - pet.snuggle(); +fn main() { + let a: &'static str = "hello"; + { + let b = String::from("world"); + let b = &b; // 'b has a shorter lifetime than 'static + debug(a, b); // a silently converts from `&'static str` into `&'b str` + } } ``` -By default, static types must match *exactly* for a program to compile. As such, -this code won't compile: - - -```rust,ignore -let mr_snuggles: Cat = ...; -love(mr_snuggles); // ERROR: expected Animal, found Cat -``` - -Mr. Snuggles is a Cat, and Cats aren't *exactly* Animals, so we can't love him! 😿 - -This is annoying because Cats *are* Animals. They support every operation -an Animal supports, so intuitively `love` shouldn't care if we pass it a `Cat`. -We should be able to just **forget** the non-animal parts of our `Cat`, as they -aren't necessary to love it. - -This is exactly the problem that *subtyping* is intended to fix. Because Cats are just -Animals **and more**, we say Cat is a *subtype* of Animal (because Cats are a *subset* -of all the Animals). Equivalently, we say that Animal is a *supertype* of Cat. -With subtypes, we can tweak our overly strict static type system -with a simple rule: anywhere a value of type `T` is expected, we will also -accept values that are subtypes of `T`. - -Or more concretely: anywhere an Animal is expected, a Cat or Dog will also work. - -As we will see throughout the rest of this section, subtyping is a lot more complicated -and subtle than this, but this simple rule is a very good 99% intuition. And unless you -write unsafe code, the compiler will automatically handle all the corner cases for you. - -But this is the Rustonomicon. We're writing unsafe code, so we need to understand how -this stuff really works, and how we can mess it up. - -The core problem is that this rule, naively applied, will lead to *meowing Dogs*. That is, -we can convince someone that a Dog is actually a Cat. This completely destroys the fabric -of our static type system, making it worse than useless (and leading to Undefined Behavior). - -Here's a simple example of this happening when we apply subtyping in a completely naive -"find and replace" way. +## Variance - -```rust,ignore -fn evil_feeder(pet: &mut Animal) { - let spike: Dog = ...; +Above, we glossed over the fact that `'static: 'b` implied that `&'static T: &'b T`. This uses a property known as variance. +It's not always as simple as this example though, to understand that let's try extend this example a bit - // `pet` is an Animal, and Dog is a subtype of Animal, - // so this should be fine, right..? - *pet = spike; +```rust,compile_fail +fn debug(a: &mut T, b: T) { + *a = b; } fn main() { - let mut mr_snuggles: Cat = ...; - evil_feeder(&mut mr_snuggles); // Replaces mr_snuggles with a Dog - mr_snuggles.meow(); // OH NO, MEOWING DOG! + let mut a: &'static str = "hello"; + { + let b = String::from("world"); + let b = &b; + debug(&mut a, b); + } } ``` -Clearly, we need a more robust system than "find and replace". That system is *variance*, -which is a set of rules governing how subtyping should compose. Most importantly, variance -defines situations where subtyping should be disabled. +This has a memory bug in it. -But before we get into variance, let's take a quick peek at where subtyping actually occurs in -Rust: *lifetimes*! - -> NOTE: The typed-ness of lifetimes is a fairly arbitrary construct that some -> disagree with. However it simplifies our analysis to treat lifetimes -> and types uniformly. - -Lifetimes are just regions of code, and regions can be partially ordered with the *contains* -(outlives) relationship. Subtyping on lifetimes is in terms of that relationship: -if `'big: 'small` ("big contains small" or "big outlives small"), then `'big` is a subtype -of `'small`. This is a large source of confusion, because it seems backwards -to many: the bigger region is a *subtype* of the smaller region. But it makes -sense if you consider our Animal example: Cat is an Animal *and more*, -just as `'big` is `'small` *and more*. - -Put another way, if someone wants a reference that lives for `'small`, -usually what they actually mean is that they want a reference that lives -for *at least* `'small`. They don't actually care if the lifetimes match -exactly. So it should be ok for us to **forget** that something lives for -`'big` and only remember that it lives for `'small`. - -The meowing dog problem for lifetimes will result in us being able to -store a short-lived reference in a place that expects a longer-lived one, -creating a dangling reference and letting us use-after-free. - -It will be useful to note that `'static`, the forever lifetime, is a subtype of -every lifetime because by definition it outlives everything. We will be using -this relationship in later examples to keep them as simple as possible. - -With all that said, we still have no idea how to actually *use* subtyping of lifetimes, -because nothing ever has type `'a`. Lifetimes only occur as part of some larger type -like `&'a u32` or `IterMut<'a, u32>`. To apply lifetime subtyping, we need to know -how to compose subtyping. Once again, we need *variance*. - -## Variance +If we were to expand this out, we'd see that we're trying to assign a `&'b str` into a `&'static str`, +but the problem is that as soon as `b` goes out of scope, `a` is now invalid, even though it's supposed to have a `'static` lifetime. -Variance is where things get a bit complicated. +However, the implementation of `debug` is valid. +Therefore, this must mean that `&mut &'static str` should **not** a *subtype* of `&mut &'b str`, +even if `'static` is a subtype of `'b`. -Variance is a property that *type constructors* have with respect to their -arguments. A type constructor in Rust is any generic type with unbound arguments. +Variance is the way that Rust defines the transitivity of subtypes through their *type constructor*. +A type constructor in Rust is any generic type with unbound arguments. For instance `Vec` is a type constructor that takes a type `T` and returns `Vec`. `&` and `&mut` are type constructors that take two inputs: a lifetime, and a type to point to. @@ -192,20 +132,18 @@ A type constructor F's *variance* is how the subtyping of its inputs affects the subtyping of its outputs. There are three kinds of variance in Rust. Given two types `Sub` and `Super`, where `Sub` is a subtype of `Super`: -* `F` is *covariant* if `F` is a subtype of `F` (subtyping "passes through") -* `F` is *contravariant* if `F` is a subtype of `F` (subtyping is "inverted") -* `F` is *invariant* otherwise (no subtyping relationship exists) +* F is **covariant** if `F` is a subtype of `F` (the subtype property is passed through) +* F is **contravariant** if `F` is a subtype of `F` (the subtype property is "inverted") +* F is **invariant** otherwise (no subtyping relationship exists) -If `F` has multiple type parameters, we can talk about the individual variances -by saying that, for example, `F` is covariant over `T` and invariant over `U`. +If we remember from the above examples, +it was ok for us to treat `&'a T` as a subtype of `&'b T` if `'a: 'b`, +therefore we can say that `&'a T` is *covariant* over `'a`. -It is very useful to keep in mind that covariance is, in practical terms, "the" -variance. Almost all consideration of variance is in terms of whether something -should be covariant or invariant. Actually witnessing contravariance is quite difficult -in Rust, though it does in fact exist. +Also, we saw that it was not ok for us to treat `&mut &'a T` as a subtype of `&mut &'b T`, +therefore we can say that `&mut T` is *invariant* over `T` -Here is a table of important variances which the rest of this section will be devoted -to trying to explain: +Here is a table of some other type constructors and their variances: | | | 'a | T | U | |---|-----------------|:---------:|:-----------------:|:---------:| From 0492daf82c2a3653341265c2075d90547faf051b Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Thu, 10 Feb 2022 14:08:59 +0000 Subject: [PATCH 28/48] clarify some points --- src/subtyping.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index ce01d15..3792cbe 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -5,14 +5,16 @@ However, a naive implementation of lifetimes would be either too restrictive, or permit undefined behavior. In order to allow flexible usage of lifetimes -while also preventing mis-use, Rust uses a combination of **Subtyping** and **Variance**. +while also preventing their misuse, Rust uses a combination of **Subtyping** and **Variance**. ## Subtyping Subtyping is the idea that one type can be a *subtype* of another. -Let's define that `A: B` is equivalent to saying '`A` is a subtype of `B`'. -What this is suggesting to us is that the set of *requirements* that `B` defines -are completely satisfied by `A`. `A` may then have more requirements. + +Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub: Super` throughout this chapter) + +What this is suggesting to us is that the set of *requirements* that `Super` defines +are completely satisfied by `Sub`. `Sub` may then have more requirements. An example of simple subtyping that exists in the language are [supertraits](https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html?highlight=supertraits#using-supertraits-to-require-one-traits-functionality-within-another-trait) @@ -72,7 +74,7 @@ Now that we have a defined set of requirements for lifetimes, we can define how `'a` may define a region larger than `'b`, but that still fits our definition. Going back to our example above, we can say that `'static: 'b`. -For now, let's accept the idea that subtypes of lifetimes can be transitive (more on this in [Variance](#variance>)), +For now, let's accept the idea that subtypes of lifetimes can be transitive (more on this in [Variance](#variance)), eg. `&'static str` is a subtype of `&'b str`, then we can let them coerce, and then the example above will compile ```rust From a43237778a577e5f162f31f9890e0b1caa78565a Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Thu, 10 Feb 2022 15:20:18 +0000 Subject: [PATCH 29/48] more explanations --- src/subtyping.md | 171 +++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 79 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index 3792cbe..0bda409 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -42,11 +42,11 @@ fn debug(a: T, b: T) { } fn main() { - let a: &'static str = "hello"; + let hello: &'static str = "hello"; { - let b = String::from("world"); - let b = &b; // 'b has a shorter lifetime than 'static - debug(a, b); + let world = String::from("world"); + let world = &world; // 'b has a shorter lifetime than 'static + debug(hello, world); } } ``` @@ -58,10 +58,10 @@ we might see the following error: error[E0308]: mismatched types --> src/main.rs:10:16 | -10 | debug(a, b); - | ^ - | | - | expected `&'static str`, found struct `&'b str` +10 | debug(hello, world); + | ^ + | | + | expected `&'static str`, found struct `&'b str` ``` This is over-restrictive. In this case, what we want is to accept any type that lives *at least as long* as `'b`. @@ -83,11 +83,11 @@ fn debug(a: T, b: T) { } fn main() { - let a: &'static str = "hello"; + let hello: &'static str = "hello"; { - let b = String::from("world"); - let b = &b; // 'b has a shorter lifetime than 'static - debug(a, b); // a silently converts from `&'static str` into `&'b str` + let world = String::from("world"); + let world = &world; // 'b has a shorter lifetime than 'static + debug(hello, world); // a silently converts from `&'static str` into `&'b str` } } ``` @@ -98,26 +98,25 @@ Above, we glossed over the fact that `'static: 'b` implied that `&'static T: &'b It's not always as simple as this example though, to understand that let's try extend this example a bit ```rust,compile_fail -fn debug(a: &mut T, b: T) { - *a = b; +fn assign(input: &mut T, val: T) { + *input = val; } fn main() { - let mut a: &'static str = "hello"; + let mut hello: &'static str = "hello"; { - let b = String::from("world"); - let b = &b; - debug(&mut a, b); + let world = String::from("world"); + assign(&mut hello, &world); } } ``` -This has a memory bug in it. +If this were to compile, this would have a memory bug. If we were to expand this out, we'd see that we're trying to assign a `&'b str` into a `&'static str`, but the problem is that as soon as `b` goes out of scope, `a` is now invalid, even though it's supposed to have a `'static` lifetime. -However, the implementation of `debug` is valid. +However, the implementation of `assign` is valid. Therefore, this must mean that `&mut &'static str` should **not** a *subtype* of `&mut &'b str`, even if `'static` is a subtype of `'b`. @@ -149,21 +148,21 @@ Here is a table of some other type constructors and their variances: | | | 'a | T | U | |---|-----------------|:---------:|:-----------------:|:---------:| -| * | `&'a T ` | covariant | covariant | | -| * | `&'a mut T` | covariant | invariant | | -| * | `Box` | | covariant | | +| | `&'a T ` | covariant | covariant | | +| | `&'a mut T` | covariant | invariant | | +| | `Box` | | covariant | | | | `Vec` | | covariant | | -| * | `UnsafeCell` | | invariant | | +| | `UnsafeCell` | | invariant | | | | `Cell` | | invariant | | -| * | `fn(T) -> U` | | **contra**variant | covariant | +| | `fn(T) -> U` | | **contra**variant | covariant | | | `*const T` | | covariant | | | | `*mut T` | | invariant | | -The types with \*'s are the ones we will be focusing on, as they are in -some sense "fundamental". All the others can be understood by analogy to the others: +Some of these can be explained simply in relation to the others: * `Vec` and all other owning pointers and collections follow the same logic as `Box` * `Cell` and all other interior mutability types follow the same logic as `UnsafeCell` +* `UnsafeCell` having interior mutability gives it the same variance properties as `&mut T` * `*const T` follows the logic of `&T` * `*mut T` follows the logic of `&mut T` (or `UnsafeCell`) @@ -177,8 +176,72 @@ For more types, see the ["Variance" section][variance-table] on the reference. > take references with specific lifetimes (as opposed to the usual "any lifetime", > which gets into higher rank lifetimes, which work independently of subtyping). -Ok, that's enough type theory! Let's try to apply the concept of variance to Rust -and look at some examples. +Now that we have some more formal understanding of variance, +let's go through some more examples in more detail. + +```rust,compile_fail +fn assign(input: &mut T, val: T) { + *input = val; +} + +fn main() { + let mut hello: &'static str = "hello"; + { + let world = String::from("world"); + assign(&mut hello, &world); + } +} +``` + +And what do we get when we run this? + +```text +error[E0597]: `world` does not live long enough + --> src/main.rs:9:28 + | +6 | let mut hello: &'static str = "hello"; + | ------------ type annotation requires that `world` is borrowed for `'static` +... +9 | assign(&mut hello, &world); + | ^^^^^^ borrowed value does not live long enough +10 | } + | - `world` dropped here while still borrowed +``` + +Good, it doesn't compile! Let's break down what's happening here in detail. + +First let's look at the `assign` function: + +```rust +fn assign(input: &mut T, val: T) { + *input = val; +} +``` + +All it does is take a mutable reference and a value and overwrite the referent with it. +What's important about this function is that it creates a type equality constraint. It +clearly says in its signature the referent and the value must be the *exact same* type. + +Meanwhile, in the caller we pass in `&mut &'static str` and `&'spike_str str`. + +Because `&mut T` is invariant over `T`, the compiler concludes it can't apply any subtyping +to the first argument, and so `T` must be exactly `&'static str`. + +This is counter to the `&T` case + +```rust +fn debug(a: T, b: T) { + println!("a = {:?} b = {:?}", a, b); +} +``` + +Where similarly `a` and `b` must have the same type `T`. +But since `&'a T` *is* covariant over `'a`, we are allowed to perform subtyping. +So the compiler decides that `&'static str` can become `&'b str` if and only if +`&'static str` is a subtype of `&'b str`, which will hold if `'static: 'b`. +This is true, so the compiler is happy to continue compiling this code. + +--- First off, let's revisit the meowing dog example: @@ -249,56 +312,6 @@ enough into the place expecting something long-lived. Here it is: -```rust,compile_fail -fn evil_feeder(input: &mut T, val: T) { - *input = val; -} - -fn main() { - let mut mr_snuggles: &'static str = "meow! :3"; // mr. snuggles forever!! - { - let spike = String::from("bark! >:V"); - let spike_str: &str = &spike; // Only lives for the block - evil_feeder(&mut mr_snuggles, spike_str); // EVIL! - } - println!("{}", mr_snuggles); // Use after free? -} -``` - -And what do we get when we run this? - -```text -error[E0597]: `spike` does not live long enough - --> src/main.rs:9:31 - | -6 | let mut mr_snuggles: &'static str = "meow! :3"; // mr. snuggles forever!! - | ------------ type annotation requires that `spike` is borrowed for `'static` -... -9 | let spike_str: &str = &spike; // Only lives for the block - | ^^^^^^ borrowed value does not live long enough -10 | evil_feeder(&mut mr_snuggles, spike_str); // EVIL! -11 | } - | - `spike` dropped here while still borrowed -``` - -Good, it doesn't compile! Let's break down what's happening here in detail. - -First let's look at the new `evil_feeder` function: - -```rust -fn evil_feeder(input: &mut T, val: T) { - *input = val; -} -``` - -All it does is take a mutable reference and a value and overwrite the referent with it. -What's important about this function is that it creates a type equality constraint. It -clearly says in its signature the referent and the value must be the *exact same* type. - -Meanwhile, in the caller we pass in `&mut &'static str` and `&'spike_str str`. - -Because `&mut T` is invariant over `T`, the compiler concludes it can't apply any subtyping -to the first argument, and so `T` must be exactly `&'static str`. The other argument is only an `&'a str`, which *is* covariant over `'a`. So the compiler adopts a constraint: `&'spike_str str` must be a subtype of `&'static str` (inclusive), From 8e129cc2a8f1e6d6edda4353b0af4f75190a6b21 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Thu, 10 Feb 2022 15:47:57 +0000 Subject: [PATCH 30/48] remove the rest of animals --- src/subtyping.md | 151 +++++------------------------------------------ 1 file changed, 15 insertions(+), 136 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index 0bda409..61bebcb 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -241,127 +241,9 @@ So the compiler decides that `&'static str` can become `&'b str` if and only if `&'static str` is a subtype of `&'b str`, which will hold if `'static: 'b`. This is true, so the compiler is happy to continue compiling this code. ---- - -First off, let's revisit the meowing dog example: - - -```rust,ignore -fn evil_feeder(pet: &mut Animal) { - let spike: Dog = ...; - - // `pet` is an Animal, and Dog is a subtype of Animal, - // so this should be fine, right..? - *pet = spike; -} - -fn main() { - let mut mr_snuggles: Cat = ...; - evil_feeder(&mut mr_snuggles); // Replaces mr_snuggles with a Dog - mr_snuggles.meow(); // OH NO, MEOWING DOG! -} -``` - -If we look at our table of variances, we see that `&mut T` is *invariant* over `T`. -As it turns out, this completely fixes the issue! With invariance, the fact that -Cat is a subtype of Animal doesn't matter; `&mut Cat` still won't be a subtype of -`&mut Animal`. The static type checker will then correctly stop us from passing -a Cat into `evil_feeder`. - -The soundness of subtyping is based on the idea that it's ok to forget unnecessary -details. But with references, there's always someone that remembers those details: -the value being referenced. That value expects those details to keep being true, -and may behave incorrectly if its expectations are violated. - -The problem with making `&mut T` covariant over `T` is that it gives us the power -to modify the original value *when we don't remember all of its constraints*. -And so, we can make someone have a Dog when they're certain they still have a Cat. - -With that established, we can easily see why `&T` being covariant over `T` *is* -sound: it doesn't let you modify the value, only look at it. Without any way to -mutate, there's no way for us to mess with any details. We can also see why -`UnsafeCell` and all the other interior mutability types must be invariant: they -make `&T` work like `&mut T`! - -Now what about the lifetime on references? Why is it ok for both kinds of references -to be covariant over their lifetimes? Well, here's a two-pronged argument: - -First and foremost, subtyping references based on their lifetimes is *the entire point -of subtyping in Rust*. The only reason we have subtyping is so we can pass -long-lived things where short-lived things are expected. So it better work! - -Second, and more seriously, lifetimes are only a part of the reference itself. The -type of the referent is shared knowledge, which is why adjusting that type in only -one place (the reference) can lead to issues. But if you shrink down a reference's -lifetime when you hand it to someone, that lifetime information isn't shared in -any way. There are now two independent references with independent lifetimes. -There's no way to mess with the original reference's lifetime using the other one. - -Or rather, the only way to mess with someone's lifetime is to build a meowing dog. -But as soon as you try to build a meowing dog, the lifetime should be wrapped up -in an invariant type, preventing the lifetime from being shrunk. To understand this -better, let's port the meowing dog problem over to real Rust. - -In the meowing dog problem we take a subtype (Cat), convert it into a supertype -(Animal), and then use that fact to overwrite the subtype with a value that satisfies -the constraints of the supertype but not the subtype (Dog). - -So with lifetimes, we want to take a long-lived thing, convert it into a -short-lived thing, and then use that to write something that doesn't live long -enough into the place expecting something long-lived. - -Here it is: - - -The other argument is only an `&'a str`, which *is* covariant over `'a`. So the compiler -adopts a constraint: `&'spike_str str` must be a subtype of `&'static str` (inclusive), -which in turn implies `'spike_str` must be a subtype of `'static` (inclusive). Which is to say, -`'spike_str` must contain `'static`. But only one thing contains `'static` -- `'static` itself! - -This is why we get an error when we try to assign `&spike` to `spike_str`. The -compiler has worked backwards to conclude `spike_str` must live forever, and `&spike` -simply can't live that long. - -So even though references are covariant over their lifetimes, they "inherit" invariance -whenever they're put into a context that could do something bad with that. In this case, -we inherited invariance as soon as we put our reference inside an `&mut T`. - -As it turns out, the argument for why it's ok for Box (and Vec, Hashmap, etc.) to -be covariant is pretty similar to the argument for why it's ok for -references to be covariant: as soon as you try to stuff them in something like a -mutable reference, they inherit invariance and you're prevented from doing anything -bad. - -However, Box makes it easier to focus on the by-value aspect of references that we -partially glossed over. - -Unlike a lot of languages which allow values to be freely aliased at all times, -Rust has a very strict rule: if you're allowed to mutate or move a value, you -are guaranteed to be the only one with access to it. - -Consider the following code: - - -```rust,ignore -let mr_snuggles: Box = ..; -let spike: Box = ..; - -let mut pet: Box; -pet = mr_snuggles; -pet = spike; -``` - -There is no problem at all with the fact that we have forgotten that `mr_snuggles` was a Cat, -or that we overwrote him with a Dog, because as soon as we moved mr_snuggles to a variable -that only knew he was an Animal, **we destroyed the only thing in the universe that -remembered he was a Cat**! - -In contrast to the argument about immutable references being soundly covariant because they -don't let you change anything, owned values can be covariant because they make you -change *everything*. There is no connection between old locations and new locations. -Applying by-value subtyping is an irreversible act of knowledge destruction, and -without any memory of how things used to be, no one can be tricked into acting on -that old information! +`Box` is also *covariant* over `T`. This would make sense, since it's supposed to be +usable the same as `&T`. If you try to mutate the box, you'll need a `&mut Box` and the +invariance of `&mut` will kick in here. Only one thing left to explain: function pointers. @@ -369,43 +251,40 @@ To see why `fn(T) -> U` should be covariant over `U`, consider the following sig ```rust,ignore -fn get_animal() -> Animal; +fn get_str() -> &'a str; ``` -This function claims to produce an Animal. As such, it is perfectly valid to +This function claims to produce a `str` bound by some liftime `'a`. As such, it is perfectly valid to provide a function with the following signature instead: ```rust,ignore -fn get_animal() -> Cat; +fn get_static() -> &'static str; ``` -After all, Cats are Animals, so always producing a Cat is a perfectly valid way -to produce Animals. Or to relate it back to real Rust: if we need a function -that is supposed to produce something that lives for `'short`, it's perfectly -fine for it to produce something that lives for `'long`. We don't care, we can -just forget that fact. +So when the function is called, all it's expecting is a `&str` which lives at least the lifetime of `'a`, +it doesn't matter if the value actually lives longer. However, the same logic does not apply to *arguments*. Consider trying to satisfy: ```rust,ignore -fn handle_animal(Animal); +fn store_ref(&'a str); ``` with: ```rust,ignore -fn handle_animal(Cat); +fn store_static(&'static str); ``` -The first function can accept Dogs, but the second function absolutely can't. +The first function can accept any string reference as long as it lives at least for `'a`, +but the second cannot accept a string reference that lives for any duration less than `'static`, +which would cause a conflict. Covariance doesn't work here. But if we flip it around, it actually *does* -work! If we need a function that can handle Cats, a function that can handle *any* -Animal will surely work fine. Or to relate it back to real Rust: if we need a -function that can handle anything that lives for at least `'long`, it's perfectly -fine for it to be able to handle anything that lives for at least `'short`. +work! If we need a function that can handle `&'static str`, a function that can handle *any* reference lifetime +will surely work fine. And that's why function types, unlike anything else in the language, are **contra**variant over their arguments. From 510938c8ac27b85fbe5c15d694b3caef8d7ac9f0 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Thu, 10 Feb 2022 16:26:08 +0000 Subject: [PATCH 31/48] remove use of transitive --- src/subtyping.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index 61bebcb..b2ceb7b 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -74,7 +74,7 @@ Now that we have a defined set of requirements for lifetimes, we can define how `'a` may define a region larger than `'b`, but that still fits our definition. Going back to our example above, we can say that `'static: 'b`. -For now, let's accept the idea that subtypes of lifetimes can be transitive (more on this in [Variance](#variance)), +For now, let's accept the idea that subtypes of lifetimes can be passed through references (more on this in [Variance](#variance)), eg. `&'static str` is a subtype of `&'b str`, then we can let them coerce, and then the example above will compile ```rust @@ -120,7 +120,7 @@ However, the implementation of `assign` is valid. Therefore, this must mean that `&mut &'static str` should **not** a *subtype* of `&mut &'b str`, even if `'static` is a subtype of `'b`. -Variance is the way that Rust defines the transitivity of subtypes through their *type constructor*. +Variance is the way that Rust defines the relationships of subtypes through their *type constructor*. A type constructor in Rust is any generic type with unbound arguments. For instance `Vec` is a type constructor that takes a type `T` and returns `Vec`. `&` and `&mut` are type constructors that take two inputs: a From aeb9d4c21db62db267ffe28ef27d0252afc3168a Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Fri, 11 Feb 2022 08:07:56 +0000 Subject: [PATCH 32/48] address some comments --- src/subtyping.md | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index b2ceb7b..e59e8a0 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -9,7 +9,7 @@ while also preventing their misuse, Rust uses a combination of **Subtyping** and ## Subtyping -Subtyping is the idea that one type can be a *subtype* of another. +Subtyping is the idea that one type can be used in place of another. Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub: Super` throughout this chapter) @@ -21,15 +21,15 @@ An example of simple subtyping that exists in the language are [supertraits](htt ```rust use std::fmt; -trait OutlinePrint: fmt::Display { - fn outline_print(&self) { - todo!() - } +pub trait Error: fmt::Display { + fn source(&self) -> Option<&(dyn Error + 'static)>; + fn description(&self) -> &str; + fn cause(&self) -> Option<&dyn Error>; } ``` -Here, we have that `OutlinePrint: fmt::Display` (`OutlinePrint` is a *subtype* of `Display`), -because it has all the requirements of `fmt::Display`, plus the `outline_print` function. +Here, we have that `Error: fmt::Display` (`Error` is a *subtype* of `Display`), +because it has all the requirements of `fmt::Display`, plus the `source`/`description`/`cause` functions. However, subtyping in traits is not that interesting in the case of Rust. Here in the nomicon, we're going to focus more with how subtyping interacts with **lifetimes** @@ -51,7 +51,7 @@ fn main() { } ``` -In an overly restrictive implementation of lifetimes, since `a` and `b` have differeing lifetimes, +In a conservative implementation of lifetimes, since `a` and `b` have differeing lifetimes, we might see the following error: ```text @@ -64,10 +64,12 @@ error[E0308]: mismatched types | expected `&'static str`, found struct `&'b str` ``` -This is over-restrictive. In this case, what we want is to accept any type that lives *at least as long* as `'b`. +This would be rather unfortunate. In this case, +what we want is to accept any type that lives *at least as long* as `'b`. Let's try using subtyping with our lifetimes. -Let's define a lifetime to have the a simple set of requirements: `'a` defines a region of code in which a value will be alive. +Let's define a lifetime to have the a simple set of requirements: +`'a` defines a region of code in which a value will be alive. Now that we have a defined set of requirements for lifetimes, we can define how they relate to each other. `'a: 'b` if and only if `'a` defines a region of code that **completely contains** `'b`. @@ -108,20 +110,24 @@ fn main() { let world = String::from("world"); assign(&mut hello, &world); } + println!("{}", hello); } ``` -If this were to compile, this would have a memory bug. +In `assign`, we are setting the `hello` reference to point to `world`. +But then `world` goes out of scope, before the later use of `hello` in the println! + +This is a classic use-after-free bug! -If we were to expand this out, we'd see that we're trying to assign a `&'b str` into a `&'static str`, -but the problem is that as soon as `b` goes out of scope, `a` is now invalid, even though it's supposed to have a `'static` lifetime. +Our first instinct might be to blame the `assign` impl, but there's really nothing wrong here. +It shouldn't be surprising that we might want to assign a `T` into a `T`. -However, the implementation of `assign` is valid. -Therefore, this must mean that `&mut &'static str` should **not** a *subtype* of `&mut &'b str`, +The problem is that we cannot assume that `&mut &'static str` and `&mut &'b str` are compatible. +This must mean that `&mut &'static str` should **not** be a *subtype* of `&mut &'b str`, even if `'static` is a subtype of `'b`. Variance is the way that Rust defines the relationships of subtypes through their *type constructor*. -A type constructor in Rust is any generic type with unbound arguments. +A type constructor is any generic type with unbound arguments. For instance `Vec` is a type constructor that takes a type `T` and returns `Vec`. `&` and `&mut` are type constructors that take two inputs: a lifetime, and a type to point to. @@ -190,6 +196,7 @@ fn main() { let world = String::from("world"); assign(&mut hello, &world); } + println!("{}", hello); } ``` From 2c8ff4f66903373f97be3cb202ecb6ef60601a7f Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Fri, 11 Feb 2022 08:24:28 +0000 Subject: [PATCH 33/48] slight restructure --- src/subtyping.md | 78 +++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index e59e8a0..089d53c 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -7,34 +7,7 @@ or permit undefined behavior. In order to allow flexible usage of lifetimes while also preventing their misuse, Rust uses a combination of **Subtyping** and **Variance**. -## Subtyping - -Subtyping is the idea that one type can be used in place of another. - -Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub: Super` throughout this chapter) - -What this is suggesting to us is that the set of *requirements* that `Super` defines -are completely satisfied by `Sub`. `Sub` may then have more requirements. - -An example of simple subtyping that exists in the language are [supertraits](https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html?highlight=supertraits#using-supertraits-to-require-one-traits-functionality-within-another-trait) - -```rust -use std::fmt; - -pub trait Error: fmt::Display { - fn source(&self) -> Option<&(dyn Error + 'static)>; - fn description(&self) -> &str; - fn cause(&self) -> Option<&dyn Error>; -} -``` - -Here, we have that `Error: fmt::Display` (`Error` is a *subtype* of `Display`), -because it has all the requirements of `fmt::Display`, plus the `source`/`description`/`cause` functions. - -However, subtyping in traits is not that interesting in the case of Rust. -Here in the nomicon, we're going to focus more with how subtyping interacts with **lifetimes** - -Take this example +Let's start with a example. ```rust fn debug(a: T, b: T) { @@ -68,16 +41,53 @@ This would be rather unfortunate. In this case, what we want is to accept any type that lives *at least as long* as `'b`. Let's try using subtyping with our lifetimes. -Let's define a lifetime to have the a simple set of requirements: +## Subtyping + +Subtyping is the idea that one type can be used in place of another. + +Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub: Super` throughout this chapter) + +What this is suggesting to us is that the set of *requirements* that `Super` defines +are completely satisfied by `Sub`. `Sub` may then have more requirements. + +An example of simple subtyping that exists in the language are [supertraits](https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html?highlight=supertraits#using-supertraits-to-require-one-traits-functionality-within-another-trait) + +```rust +use std::fmt; + +pub trait Error: fmt::Display { + fn source(&self) -> Option<&(dyn Error + 'static)>; + fn description(&self) -> &str; + fn cause(&self) -> Option<&dyn Error>; +} +``` + +Here, we have that `Error: fmt::Display` (`Error` is a *subtype* of `Display`), +because it has all the requirements of `fmt::Display`, plus the `source`/`description`/`cause` functions. + +However, subtyping in traits is not that interesting. +Here in the nomicon, we're going to focus more with how subtyping interacts with lifetimes + +Let's define a lifetime to be the simple requirement: `'a` defines a region of code in which a value will be alive. Now that we have a defined set of requirements for lifetimes, we can define how they relate to each other. -`'a: 'b` if and only if `'a` defines a region of code that **completely contains** `'b`. +`'long: 'short` if and only if `'long` defines a region of code that **completely contains** `'short`. -`'a` may define a region larger than `'b`, but that still fits our definition. -Going back to our example above, we can say that `'static: 'b`. +`'long` may define a region larger than `'short`, but that still fits our definition. -For now, let's accept the idea that subtypes of lifetimes can be passed through references (more on this in [Variance](#variance)), -eg. `&'static str` is a subtype of `&'b str`, then we can let them coerce, and then the example above will compile +> As we will see throughout the rest of this chapter, +subtyping is a lot more complicated and subtle than this, +but this simple rule is a very good 99% intuition. +And unless you write unsafe code, the compiler will automatically handle all the corner cases for you. + +> But this is the Rustonomicon. We're writing unsafe code, +so we need to understand how this stuff really works, and how we can mess it up. + +Going back to our example above, we can say that `'static: 'b`. +For now, let's also accept the idea that subtypes of lifetimes can be passed through references +(more on this in [Variance](#variance)), +eg. `&'static str` is a subtype of `&'b str`, then we can let them coerce, +and then the example above will compile ```rust fn debug(a: T, b: T) { From 8f88efd850c8221b5a98917caec13196a212d19f Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Fri, 11 Feb 2022 09:02:57 +0000 Subject: [PATCH 34/48] add demos for box and fn --- src/subtyping.md | 61 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index 089d53c..c0a8e5f 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -120,7 +120,7 @@ fn main() { let world = String::from("world"); assign(&mut hello, &world); } - println!("{}", hello); + println!("{}", hello); // use after free 😿 } ``` @@ -258,9 +258,24 @@ So the compiler decides that `&'static str` can become `&'b str` if and only if `&'static str` is a subtype of `&'b str`, which will hold if `'static: 'b`. This is true, so the compiler is happy to continue compiling this code. -`Box` is also *covariant* over `T`. This would make sense, since it's supposed to be -usable the same as `&T`. If you try to mutate the box, you'll need a `&mut Box` and the -invariance of `&mut` will kick in here. +As it turns out, the argument for why it's ok for Box (and Vec, Hashmap, etc.) to be covariant is pretty similar to the argument for why it's ok for lifetimes to be covariant: as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad. + +However Box makes it easier to focus on by-value aspect of references that we partially glossed over. + +Unlike a lot of languages which allow values to be freely aliased at all times, Rust has a very strict rule: if you're allowed to mutate or move a value, you are guaranteed to be the only one with access to it. + +Consider the following code: + +```rust,ignore +let hello: Box<&'static str> = Box::new("hello"); + +let mut world: Box<&'b str>; +world = hello; +``` + +There is no problem at all with the fact that we have forgotten that `hello` was alive for `'static`, +because as soon as we moved `hello` to a variable that only knew he it was alive for `'b`, +**we destroyed the only thing in the universe that remembered it lived for longer**! Only one thing left to explain: function pointers. @@ -303,6 +318,44 @@ Covariance doesn't work here. But if we flip it around, it actually *does* work! If we need a function that can handle `&'static str`, a function that can handle *any* reference lifetime will surely work fine. +Let's see this in practice + +```rust,compile_fail +# use std::cell::RefCell; +thread_local! { + pub static StaticVecs: RefCell> = RefCell::new(Vec::new()); +} + +/// saves the input given into a thread local `Vec<&'static str>` +fn store(input: &'static str) { + StaticVecs.with(|v| { + v.borrow_mut().push(input); + }) +} + +/// Calls the function with it's input (must have the same lifetime!) +fn demo<'a>(input: &'a str, f: fn(&'a str)) { + f(input); +} + +fn main() { + demo("hello", store); // "hello" is 'static. Can call `store` fine + + { + let smuggle = String::from("smuggle"); + + // `&smuggle` is not static. If we were to call `store` with `&smuggle`, + // we would have pushed an invalid lifetime into the `StaticVecs`. + // Therefore, `fn(&'static str)` cannot be a subtype of `fn(&'a str)` + demo(&smuggle, store); + } + + StaticVecs.with(|v| { + println!("{:?}", v.borrow()); // use after free 😿 + }); +} +``` + And that's why function types, unlike anything else in the language, are **contra**variant over their arguments. From ea950766dd92bd4ac6e06081c86f60935449ec01 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 09:14:29 +0000 Subject: [PATCH 35/48] address some grammatical comments --- src/subtyping.md | 55 ++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index c0a8e5f..5645326 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -5,12 +5,12 @@ However, a naive implementation of lifetimes would be either too restrictive, or permit undefined behavior. In order to allow flexible usage of lifetimes -while also preventing their misuse, Rust uses a combination of **Subtyping** and **Variance**. +while also preventing their misuse, Rust uses **subtyping** and **variance**. -Let's start with a example. +Let's start with an example. ```rust -fn debug(a: T, b: T) { +fn debug<'a>(a: &'a str, b: &'a str) { println!("a = {:?} b = {:?}", a, b); } @@ -18,13 +18,13 @@ fn main() { let hello: &'static str = "hello"; { let world = String::from("world"); - let world = &world; // 'b has a shorter lifetime than 'static + let world = &world; // 'world has a shorter lifetime than 'static debug(hello, world); } } ``` -In a conservative implementation of lifetimes, since `a` and `b` have differeing lifetimes, +In a conservative implementation of lifetimes, since `hello` and `world` have differing lifetimes, we might see the following error: ```text @@ -34,23 +34,25 @@ error[E0308]: mismatched types 10 | debug(hello, world); | ^ | | - | expected `&'static str`, found struct `&'b str` + | expected `&'static str`, found struct `&'world str` ``` This would be rather unfortunate. In this case, -what we want is to accept any type that lives *at least as long* as `'b`. +what we want is to accept any type that lives *at least as long* as `'world`. Let's try using subtyping with our lifetimes. ## Subtyping Subtyping is the idea that one type can be used in place of another. -Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub: Super` throughout this chapter) +Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub: Super` throughout this chapter). What this is suggesting to us is that the set of *requirements* that `Super` defines are completely satisfied by `Sub`. `Sub` may then have more requirements. -An example of simple subtyping that exists in the language are [supertraits](https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html?highlight=supertraits#using-supertraits-to-require-one-traits-functionality-within-another-trait) +An example of simple subtyping that exists in the language is [supertraits][supertraits]: + +[supertraits]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-supertraits-to-require-one-traits-functionality-within-another-trait ```rust use std::fmt; @@ -66,12 +68,15 @@ Here, we have that `Error: fmt::Display` (`Error` is a *subtype* of `Display`), because it has all the requirements of `fmt::Display`, plus the `source`/`description`/`cause` functions. However, subtyping in traits is not that interesting. -Here in the nomicon, we're going to focus more with how subtyping interacts with lifetimes +Here in the Rustonomicon, we're going to focus more with how subtyping interacts with lifetimes. Let's define a lifetime to be the simple requirement: -`'a` defines a region of code in which a value will be alive. -Now that we have a defined set of requirements for lifetimes, we can define how they relate to each other. -`'long: 'short` if and only if `'long` defines a region of code that **completely contains** `'short`. + +> `'a` defines a region of code. + +Now that we have a defined set of requirements for lifetimes, we can define how they relate to each other: + +> `'long : 'short` if and only if `'long` defines a region of code that **completely contains** `'short`. `'long` may define a region larger than `'short`, but that still fits our definition. @@ -83,11 +88,11 @@ And unless you write unsafe code, the compiler will automatically handle all the > But this is the Rustonomicon. We're writing unsafe code, so we need to understand how this stuff really works, and how we can mess it up. -Going back to our example above, we can say that `'static: 'b`. +Going back to our example above, we can say that `'static : 'world`. For now, let's also accept the idea that subtypes of lifetimes can be passed through references (more on this in [Variance](#variance)), -eg. `&'static str` is a subtype of `&'b str`, then we can let them coerce, -and then the example above will compile +_e.g._ `&'static str` is a subtype of `&'world str`, then we can let a `&'static str` "downgrade" into a `&'world str`. +With that, the example above will compile: ```rust fn debug(a: T, b: T) { @@ -98,16 +103,16 @@ fn main() { let hello: &'static str = "hello"; { let world = String::from("world"); - let world = &world; // 'b has a shorter lifetime than 'static - debug(hello, world); // a silently converts from `&'static str` into `&'b str` + let world = &world; // 'world has a shorter lifetime than 'static + debug(hello, world); // hello silently downgrades from `&'static str` into `&'world str` } } ``` ## Variance -Above, we glossed over the fact that `'static: 'b` implied that `&'static T: &'b T`. This uses a property known as variance. -It's not always as simple as this example though, to understand that let's try extend this example a bit +Above, we glossed over the fact that `'static : 'b` implied that `&'static T : &'b T`. This uses a property known as _variance_. +It's not always as simple as this example, though. To understand that, let's try to extend this example a bit: ```rust,compile_fail fn assign(input: &mut T, val: T) { @@ -133,11 +138,11 @@ Our first instinct might be to blame the `assign` impl, but there's really nothi It shouldn't be surprising that we might want to assign a `T` into a `T`. The problem is that we cannot assume that `&mut &'static str` and `&mut &'b str` are compatible. -This must mean that `&mut &'static str` should **not** be a *subtype* of `&mut &'b str`, +This means that `&mut &'static str` **cannot** be a *subtype* of `&mut &'b str`, even if `'static` is a subtype of `'b`. -Variance is the way that Rust defines the relationships of subtypes through their *type constructor*. -A type constructor is any generic type with unbound arguments. +Variance is the concept that Rust borrows to define relationships about subtypes through their *type constructor*s. +A type constructor is any generic item in Rust. For instance `Vec` is a type constructor that takes a type `T` and returns `Vec`. `&` and `&mut` are type constructors that take two inputs: a lifetime, and a type to point to. @@ -244,7 +249,7 @@ Meanwhile, in the caller we pass in `&mut &'static str` and `&'spike_str str`. Because `&mut T` is invariant over `T`, the compiler concludes it can't apply any subtyping to the first argument, and so `T` must be exactly `&'static str`. -This is counter to the `&T` case +This is counter to the `&T` case: ```rust fn debug(a: T, b: T) { @@ -252,7 +257,7 @@ fn debug(a: T, b: T) { } ``` -Where similarly `a` and `b` must have the same type `T`. +where similarly `a` and `b` must have the same type `T`. But since `&'a T` *is* covariant over `'a`, we are allowed to perform subtyping. So the compiler decides that `&'static str` can become `&'b str` if and only if `&'static str` is a subtype of `&'b str`, which will hold if `'static: 'b`. From 86b1c8759465bdc6b2d4a7edc03e7691c990a11f Mon Sep 17 00:00:00 2001 From: Timo Date: Sat, 2 Apr 2022 15:42:09 -0400 Subject: [PATCH 36/48] Copy-edit subtyping.md --- src/subtyping.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index 5645326..a4673ea 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -244,7 +244,7 @@ All it does is take a mutable reference and a value and overwrite the referent w What's important about this function is that it creates a type equality constraint. It clearly says in its signature the referent and the value must be the *exact same* type. -Meanwhile, in the caller we pass in `&mut &'static str` and `&'spike_str str`. +Meanwhile, in the caller we pass in `&mut &'static str` and `&'world str`. Because `&mut T` is invariant over `T`, the compiler concludes it can't apply any subtyping to the first argument, and so `T` must be exactly `&'static str`. @@ -263,9 +263,9 @@ So the compiler decides that `&'static str` can become `&'b str` if and only if `&'static str` is a subtype of `&'b str`, which will hold if `'static: 'b`. This is true, so the compiler is happy to continue compiling this code. -As it turns out, the argument for why it's ok for Box (and Vec, Hashmap, etc.) to be covariant is pretty similar to the argument for why it's ok for lifetimes to be covariant: as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad. +As it turns out, the argument for why it's ok for Box (and Vec, HashMap, etc.) to be covariant is pretty similar to the argument for why it's ok for lifetimes to be covariant: as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad. -However Box makes it easier to focus on by-value aspect of references that we partially glossed over. +However Box makes it easier to focus on the by-value aspect of references that we partially glossed over. Unlike a lot of languages which allow values to be freely aliased at all times, Rust has a very strict rule: if you're allowed to mutate or move a value, you are guaranteed to be the only one with access to it. @@ -279,7 +279,7 @@ world = hello; ``` There is no problem at all with the fact that we have forgotten that `hello` was alive for `'static`, -because as soon as we moved `hello` to a variable that only knew he it was alive for `'b`, +because as soon as we moved `hello` to a variable that only knew it was alive for `'b`, **we destroyed the only thing in the universe that remembered it lived for longer**! Only one thing left to explain: function pointers. From 15174604f942a930583596d795f459ab5145f220 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 11 Apr 2022 10:14:08 +0100 Subject: [PATCH 37/48] remove supertraits replace subtyping syntax remove type constructors --- src/subtyping.md | 77 +++++++++++++++++------------------------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index a4673ea..97b4ebd 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -10,6 +10,7 @@ while also preventing their misuse, Rust uses **subtyping** and **variance**. Let's start with an example. ```rust +// Note: debug expects two parameters with the *same* lifetime fn debug<'a>(a: &'a str, b: &'a str) { println!("a = {:?} b = {:?}", a, b); } @@ -45,38 +46,18 @@ Let's try using subtyping with our lifetimes. Subtyping is the idea that one type can be used in place of another. -Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub: Super` throughout this chapter). +Let's define that `Sub` is a subtype of `Super` (we'll be using the notation `Sub <: Super` throughout this chapter). What this is suggesting to us is that the set of *requirements* that `Super` defines are completely satisfied by `Sub`. `Sub` may then have more requirements. -An example of simple subtyping that exists in the language is [supertraits][supertraits]: - -[supertraits]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-supertraits-to-require-one-traits-functionality-within-another-trait - -```rust -use std::fmt; - -pub trait Error: fmt::Display { - fn source(&self) -> Option<&(dyn Error + 'static)>; - fn description(&self) -> &str; - fn cause(&self) -> Option<&dyn Error>; -} -``` - -Here, we have that `Error: fmt::Display` (`Error` is a *subtype* of `Display`), -because it has all the requirements of `fmt::Display`, plus the `source`/`description`/`cause` functions. - -However, subtyping in traits is not that interesting. -Here in the Rustonomicon, we're going to focus more with how subtyping interacts with lifetimes. - -Let's define a lifetime to be the simple requirement: +Now, in order to use subtyping with lifetimes, we need to define the requirement of a lifetime: > `'a` defines a region of code. Now that we have a defined set of requirements for lifetimes, we can define how they relate to each other: -> `'long : 'short` if and only if `'long` defines a region of code that **completely contains** `'short`. +> `'long <: 'short` if and only if `'long` defines a region of code that **completely contains** `'short`. `'long` may define a region larger than `'short`, but that still fits our definition. @@ -88,7 +69,7 @@ And unless you write unsafe code, the compiler will automatically handle all the > But this is the Rustonomicon. We're writing unsafe code, so we need to understand how this stuff really works, and how we can mess it up. -Going back to our example above, we can say that `'static : 'world`. +Going back to our example above, we can say that `'static <: 'world`. For now, let's also accept the idea that subtypes of lifetimes can be passed through references (more on this in [Variance](#variance)), _e.g._ `&'static str` is a subtype of `&'world str`, then we can let a `&'static str` "downgrade" into a `&'world str`. @@ -111,7 +92,7 @@ fn main() { ## Variance -Above, we glossed over the fact that `'static : 'b` implied that `&'static T : &'b T`. This uses a property known as _variance_. +Above, we glossed over the fact that `'static <: 'b` implied that `&'static T <: &'b T`. This uses a property known as _variance_. It's not always as simple as this example, though. To understand that, let's try to extend this example a bit: ```rust,compile_fail @@ -141,43 +122,39 @@ The problem is that we cannot assume that `&mut &'static str` and `&mut &'b str` This means that `&mut &'static str` **cannot** be a *subtype* of `&mut &'b str`, even if `'static` is a subtype of `'b`. -Variance is the concept that Rust borrows to define relationships about subtypes through their *type constructor*s. -A type constructor is any generic item in Rust. -For instance `Vec` is a type constructor that takes a type `T` and returns -`Vec`. `&` and `&mut` are type constructors that take two inputs: a -lifetime, and a type to point to. +Variance is the concept that Rust borrows to define relationships about subtypes through their generic parameters. -> NOTE: For convenience we will often refer to `F` as a type constructor just so +> NOTE: For convenience we will define a generic type `F` so > that we can easily talk about `T`. Hopefully this is clear in context. -A type constructor F's *variance* is how the subtyping of its inputs affects the +The type `F`'s *variance* is how the subtyping of its inputs affects the subtyping of its outputs. There are three kinds of variance in Rust. Given two types `Sub` and `Super`, where `Sub` is a subtype of `Super`: -* F is **covariant** if `F` is a subtype of `F` (the subtype property is passed through) -* F is **contravariant** if `F` is a subtype of `F` (the subtype property is "inverted") -* F is **invariant** otherwise (no subtyping relationship exists) +* `F` is **covariant** if `F` is a subtype of `F` (the subtype property is passed through) +* `F` is **contravariant** if `F` is a subtype of `F` (the subtype property is "inverted") +* `F` is **invariant** otherwise (no subtyping relationship exists) If we remember from the above examples, -it was ok for us to treat `&'a T` as a subtype of `&'b T` if `'a: 'b`, +it was ok for us to treat `&'a T` as a subtype of `&'b T` if `'a <: 'b`, therefore we can say that `&'a T` is *covariant* over `'a`. -Also, we saw that it was not ok for us to treat `&mut &'a T` as a subtype of `&mut &'b T`, +Also, we saw that it was not ok for us to treat `&mut &'a U` as a subtype of `&mut &'b U`, therefore we can say that `&mut T` is *invariant* over `T` -Here is a table of some other type constructors and their variances: +Here is a table of some other generic types and their variances: -| | | 'a | T | U | -|---|-----------------|:---------:|:-----------------:|:---------:| -| | `&'a T ` | covariant | covariant | | -| | `&'a mut T` | covariant | invariant | | -| | `Box` | | covariant | | -| | `Vec` | | covariant | | -| | `UnsafeCell` | | invariant | | -| | `Cell` | | invariant | | -| | `fn(T) -> U` | | **contra**variant | covariant | -| | `*const T` | | covariant | | -| | `*mut T` | | invariant | | +| | 'a | T | U | +|-----------------|:---------:|:-----------------:|:---------:| +| `&'a T ` | covariant | covariant | | +| `&'a mut T` | covariant | invariant | | +| `Box` | | covariant | | +| `Vec` | | covariant | | +| `UnsafeCell` | | invariant | | +| `Cell` | | invariant | | +| `fn(T) -> U` | | **contra**variant | covariant | +| `*const T` | | covariant | | +| `*mut T` | | invariant | | Some of these can be explained simply in relation to the others: @@ -260,7 +237,7 @@ fn debug(a: T, b: T) { where similarly `a` and `b` must have the same type `T`. But since `&'a T` *is* covariant over `'a`, we are allowed to perform subtyping. So the compiler decides that `&'static str` can become `&'b str` if and only if -`&'static str` is a subtype of `&'b str`, which will hold if `'static: 'b`. +`&'static str` is a subtype of `&'b str`, which will hold if `'static <: 'b`. This is true, so the compiler is happy to continue compiling this code. As it turns out, the argument for why it's ok for Box (and Vec, HashMap, etc.) to be covariant is pretty similar to the argument for why it's ok for lifetimes to be covariant: as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad. From 54ca7d1a34e24ee158a524ddfbe66441198a764c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 27 May 2023 18:54:51 -0700 Subject: [PATCH 38/48] Apply some review suggestions. --- src/subtyping.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index 97b4ebd..f63b532 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -12,7 +12,7 @@ Let's start with an example. ```rust // Note: debug expects two parameters with the *same* lifetime fn debug<'a>(a: &'a str, b: &'a str) { - println!("a = {:?} b = {:?}", a, b); + println!("a = {a:?} b = {b:?}"); } fn main() { @@ -25,7 +25,7 @@ fn main() { } ``` -In a conservative implementation of lifetimes, since `hello` and `world` have differing lifetimes, +In a conservative implementation of lifetimes, since `hello` and `world` have different lifetimes, we might see the following error: ```text @@ -72,12 +72,12 @@ so we need to understand how this stuff really works, and how we can mess it up. Going back to our example above, we can say that `'static <: 'world`. For now, let's also accept the idea that subtypes of lifetimes can be passed through references (more on this in [Variance](#variance)), -_e.g._ `&'static str` is a subtype of `&'world str`, then we can let a `&'static str` "downgrade" into a `&'world str`. +_e.g._ `&'static str` is a subtype of `&'world str`, then we can "downgrade" `&'static str` into a `&'world str`. With that, the example above will compile: ```rust -fn debug(a: T, b: T) { - println!("a = {:?} b = {:?}", a, b); +fn debug<'a>(a: &'a str, b: &'a str) { + println!("a = {a:?} b = {b:?}"); } fn main() { @@ -95,7 +95,7 @@ fn main() { Above, we glossed over the fact that `'static <: 'b` implied that `&'static T <: &'b T`. This uses a property known as _variance_. It's not always as simple as this example, though. To understand that, let's try to extend this example a bit: -```rust,compile_fail +```rust,compile_fail,E0597 fn assign(input: &mut T, val: T) { *input = val; } @@ -106,7 +106,7 @@ fn main() { let world = String::from("world"); assign(&mut hello, &world); } - println!("{}", hello); // use after free 😿 + println!("{hello}"); // use after free 😿 } ``` @@ -177,7 +177,7 @@ For more types, see the ["Variance" section][variance-table] on the reference. Now that we have some more formal understanding of variance, let's go through some more examples in more detail. -```rust,compile_fail +```rust,compile_fail,E0597 fn assign(input: &mut T, val: T) { *input = val; } @@ -188,7 +188,7 @@ fn main() { let world = String::from("world"); assign(&mut hello, &world); } - println!("{}", hello); + println!("{hello}"); } ``` @@ -230,7 +230,7 @@ This is counter to the `&T` case: ```rust fn debug(a: T, b: T) { - println!("a = {:?} b = {:?}", a, b); + println!("a = {a:?} b = {b:?}"); } ``` From c369e4b489332f8721fbae630354fa83385d457d Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sun, 4 Jun 2023 16:21:07 +0200 Subject: [PATCH 39/48] phantom-data: Add `Send` and `Sync` columns (#411) --- src/phantom-data.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/phantom-data.md b/src/phantom-data.md index ca1c2c2..449d9e7 100644 --- a/src/phantom-data.md +++ b/src/phantom-data.md @@ -24,7 +24,7 @@ We do this using `PhantomData`, which is a special marker type. `PhantomData` consumes no space, but simulates a field of the given type for the purpose of static analysis. This was deemed to be less error-prone than explicitly telling the type-system the kind of variance that you want, while also providing other -useful things such as the information needed by drop check. +useful things such as auto traits and the information needed by drop check. Iter logically contains a bunch of `&'a T`s, so this is exactly what we tell the `PhantomData` to simulate: @@ -234,14 +234,14 @@ standard library made a utility for itself called `Unique` which: Here’s a table of all the wonderful ways `PhantomData` could be used: -| Phantom type | `'a` | `T` | -|-----------------------------|-----------|---------------------------| -| `PhantomData` | - | covariant (with drop check) | -| `PhantomData<&'a T>` | covariant | covariant | -| `PhantomData<&'a mut T>` | covariant | invariant | -| `PhantomData<*const T>` | - | covariant | -| `PhantomData<*mut T>` | - | invariant | -| `PhantomData` | - | contravariant | -| `PhantomData T>` | - | covariant | -| `PhantomData T>` | - | invariant | -| `PhantomData>` | invariant | - | +| Phantom type | `'a` | `T` | `Send` | `Sync` | +|-----------------------------|-----------|-----------------------------|-----------|-----------| +| `PhantomData` | - | covariant (with drop check) | `T: Send` | `T: Sync` | +| `PhantomData<&'a T>` | covariant | covariant | `T: Sync` | `T: Sync` | +| `PhantomData<&'a mut T>` | covariant | invariant | `T: Send` | `T: Sync` | +| `PhantomData<*const T>` | - | covariant | - | - | +| `PhantomData<*mut T>` | - | invariant | - | - | +| `PhantomData` | - | contravariant | `Send` | `Sync` | +| `PhantomData T>` | - | covariant | `Send` | `Sync` | +| `PhantomData T>` | - | invariant | `Send` | `Sync` | +| `PhantomData>` | invariant | - | `Send` | - | From 302b995bcb24b70fd883980fd174738c3a10b705 Mon Sep 17 00:00:00 2001 From: Eva Pace Date: Wed, 5 Jul 2023 13:08:32 -0300 Subject: [PATCH 40/48] Minor improvements (#414) --- src/atomics.md | 2 +- src/exception-safety.md | 4 ++-- src/leaking.md | 6 +++--- src/vec/vec-final.md | 2 +- src/vec/vec-zsts.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/atomics.md b/src/atomics.md index 6aef6ae..72a2d56 100644 --- a/src/atomics.md +++ b/src/atomics.md @@ -76,7 +76,7 @@ For instance, say we convince the compiler to emit this logic: ```text initial state: x = 0, y = 1 -THREAD 1 THREAD2 +THREAD 1 THREAD 2 y = 3; if x == 1 { x = 1; y *= 2; } diff --git a/src/exception-safety.md b/src/exception-safety.md index ca1a394..8404bb8 100644 --- a/src/exception-safety.md +++ b/src/exception-safety.md @@ -161,9 +161,9 @@ impl<'a, T> Hole<'a, T> { unsafe { let elt = ptr::read(&data[pos]); Hole { - data: data, + data, elt: Some(elt), - pos: pos, + pos, } } } diff --git a/src/leaking.md b/src/leaking.md index ea29595..d90fed5 100644 --- a/src/leaking.md +++ b/src/leaking.md @@ -134,10 +134,10 @@ impl Rc { // Wouldn't it be nice if heap::allocate worked like this? let ptr = heap::allocate::>(); ptr::write(ptr, RcBox { - data: data, + data, ref_count: 1, }); - Rc { ptr: ptr } + Rc { ptr } } } @@ -194,7 +194,7 @@ pub fn scoped<'a, F>(f: F) -> JoinGuard<'a> ``` Here `f` is some closure for the other thread to execute. Saying that -`F: Send +'a` is saying that it closes over data that lives for `'a`, and it +`F: Send + 'a` is saying that it closes over data that lives for `'a`, and it either owns that data or the data was Sync (implying `&data` is Send). Because JoinGuard has a lifetime, it keeps all the data it closes over diff --git a/src/vec/vec-final.md b/src/vec/vec-final.md index 696391d..e680e0d 100644 --- a/src/vec/vec-final.md +++ b/src/vec/vec-final.md @@ -23,7 +23,7 @@ impl RawVec { // `NonNull::dangling()` doubles as "unallocated" and "zero-sized allocation" RawVec { ptr: NonNull::dangling(), - cap: cap, + cap, } } diff --git a/src/vec/vec-zsts.md b/src/vec/vec-zsts.md index 8f25297..6715f94 100644 --- a/src/vec/vec-zsts.md +++ b/src/vec/vec-zsts.md @@ -39,7 +39,7 @@ impl RawVec { // `NonNull::dangling()` doubles as "unallocated" and "zero-sized allocation" RawVec { ptr: NonNull::dangling(), - cap: cap, + cap, } } From 40b55e78bda6adbee7377d7f71e1a95e7cc6dfb5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 6 Sep 2023 08:42:13 +0200 Subject: [PATCH 41/48] specify which integer overflows we mean --- src/what-unsafe-does.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/what-unsafe-does.md b/src/what-unsafe-does.md index 64694ee..cd3d1c1 100644 --- a/src/what-unsafe-does.md +++ b/src/what-unsafe-does.md @@ -72,7 +72,7 @@ Rust considers it "safe" to: * Have a [race condition][race] * Leak memory * Fail to call destructors -* Overflow integers +* Overflow integers (with the built-in operators such as `+` etc.) * Abort the program * Delete the production database From a94926b1517721633917c470554011ca5509006c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 6 Sep 2023 08:44:52 +0200 Subject: [PATCH 42/48] remove 'fail to call destructors' from okay-list --- src/what-unsafe-does.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/what-unsafe-does.md b/src/what-unsafe-does.md index 64694ee..91be999 100644 --- a/src/what-unsafe-does.md +++ b/src/what-unsafe-does.md @@ -71,7 +71,6 @@ Rust considers it "safe" to: * Deadlock * Have a [race condition][race] * Leak memory -* Fail to call destructors * Overflow integers * Abort the program * Delete the production database From ddfa4214487686e91b21aa29afb972c08a8f0d5b Mon Sep 17 00:00:00 2001 From: kadiwa Date: Fri, 22 Sep 2023 19:04:10 +0200 Subject: [PATCH 43/48] Fill "Beneath `std`" (#413) --- src/beneath-std.md | 114 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 8 deletions(-) diff --git a/src/beneath-std.md b/src/beneath-std.md index 6f05182..02a02bd 100644 --- a/src/beneath-std.md +++ b/src/beneath-std.md @@ -1,10 +1,108 @@ -# Beneath std +# Beneath `std` -This section documents (or will document) features that are provided by the standard library and -that `#![no_std]` developers have to deal with (i.e. provide) to build `#![no_std]` binary crates. A -(likely incomplete) list of such features is shown below: +This section documents features that are normally provided by the `std` crate and +that `#![no_std]` developers have to deal with (i.e. provide) to build +`#![no_std]` binary crates. -- `#[lang = "eh_personality"]` -- `#[lang = "start"]` -- `#[lang = "termination"]` -- `#[panic_implementation]` +## Using `libc` + +In order to build a `#[no_std]` executable we will need `libc` as a dependency. +We can specify this using our `Cargo.toml` file: + +```toml +[dependencies] +libc = { version = "0.2.146", default-features = false } +``` + +Note that the default features have been disabled. This is a critical step - +**the default features of `libc` include the `std` crate and so must be +disabled.** + +Alternatively, we can use the unstable `rustc_private` private feature together +with an `extern crate libc;` declaration as shown in the examples below. + +## Writing an executable without `std` + +We will probably need a nightly version of the compiler to produce +a `#![no_std]` executable because on many platforms, we have to provide the +`eh_personality` [lang item], which is unstable. + +Controlling the entry point is possible in two ways: the `#[start]` attribute, +or overriding the default shim for the C `main` function with your own. +Additionally, it's required to define a [panic handler function](panic-handler.html). + +The function marked `#[start]` is passed the command line parameters +in the same format as C (aside from the exact integer types being used): + +```rust +#![feature(start, lang_items, core_intrinsics, rustc_private)] +#![allow(internal_features)] +#![no_std] + +// Necessary for `panic = "unwind"` builds on some platforms. +#![feature(panic_unwind)] +extern crate unwind; + +// Pull in the system libc library for what crt0.o likely requires. +extern crate libc; + +use core::panic::PanicInfo; + +// Entry point for this program. +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} + +// These functions are used by the compiler, but not for an empty program like this. +// They are normally provided by `std`. +#[lang = "eh_personality"] +fn rust_eh_personality() {} +#[panic_handler] +fn panic_handler(_info: &PanicInfo) -> ! { core::intrinsics::abort() } +``` + +To override the compiler-inserted `main` shim, we have to disable it +with `#![no_main]` and then create the appropriate symbol with the +correct ABI and the correct name, which requires overriding the +compiler's name mangling too: + +```rust +#![feature(lang_items, core_intrinsics, rustc_private)] +#![allow(internal_features)] +#![no_std] +#![no_main] + +// Necessary for `panic = "unwind"` builds on some platforms. +#![feature(panic_unwind)] +extern crate unwind; + +// Pull in the system libc library for what crt0.o likely requires. +extern crate libc; + +use core::ffi::{c_char, c_int}; +use core::panic::PanicInfo; + +// Entry point for this program. +#[no_mangle] // ensure that this symbol is included in the output as `main` +extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int { + 0 +} + +// These functions are used by the compiler, but not for an empty program like this. +// They are normally provided by `std`. +#[lang = "eh_personality"] +fn rust_eh_personality() {} +#[panic_handler] +fn panic_handler(_info: &PanicInfo) -> ! { core::intrinsics::abort() } +``` + +If you are working with a target that doesn't have binary releases of the +standard library available via rustup (this probably means you are building the +`core` crate yourself) and need compiler-rt intrinsics (i.e. you are probably +getting linker errors when building an executable: +``undefined reference to `__aeabi_memcpy'``), you need to manually link to the +[`compiler_builtins` crate] to get those intrinsics and solve the linker errors. + +[`compiler_builtins` crate]: https://crates.io/crates/compiler_builtins +[lang item]: https://doc.rust-lang.org/nightly/unstable-book/language-features/lang-items.html From 1842257814919fa62e81bdecd5e8f95be2839dbb Mon Sep 17 00:00:00 2001 From: Alex Abdugafarov Date: Tue, 17 Oct 2023 20:11:58 +0500 Subject: [PATCH 44/48] Fixed `Hole::get` marked as unsafe in `exception-safety.md` (#427) --- src/exception-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exception-safety.md b/src/exception-safety.md index 8404bb8..762b38b 100644 --- a/src/exception-safety.md +++ b/src/exception-safety.md @@ -172,7 +172,7 @@ impl<'a, T> Hole<'a, T> { fn removed(&self) -> &T { self.elt.as_ref().unwrap() } - unsafe fn get(&self, index: usize) -> &T { &self.data[index] } + fn get(&self, index: usize) -> &T { &self.data[index] } unsafe fn move_to(&mut self, index: usize) { let index_ptr: *const _ = &self.data[index]; From 0e589061c89ef5aa85d6b04a2bfe95a9d6543add Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 19 Nov 2023 00:36:09 -0600 Subject: [PATCH 45/48] Reword the section on general race conditions The section on preventing general race conditions is a bit hand wavy. Change wording to be more concrete, and add an example of Rust preventing general races in a very specific case. --- src/races.md | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/races.md b/src/races.md index cb78ac6..d5f1ea0 100644 --- a/src/races.md +++ b/src/races.md @@ -6,26 +6,28 @@ Safe Rust guarantees an absence of data races, which are defined as: * one or more of them is a write * one or more of them is unsynchronized -A data race has Undefined Behavior, and is therefore impossible to perform -in Safe Rust. Data races are *mostly* prevented through Rust's ownership system: +A data race has Undefined Behavior, and is therefore impossible to perform in +Safe Rust. Data races are *mostly* prevented through Rust's ownership system: it's impossible to alias a mutable reference, so it's impossible to perform a data race. Interior mutability makes this more complicated, which is largely why -we have the Send and Sync traits (see below). +we have the Send and Sync traits (see the next section for more on this). **However Rust does not prevent general race conditions.** -This is pretty fundamentally impossible, and probably honestly undesirable. Your -hardware is racy, your OS is racy, the other programs on your computer are racy, -and the world this all runs in is racy. Any system that could genuinely claim to -prevent *all* race conditions would be pretty awful to use, if not just -incorrect. +This is mathematically impossible in situations where you do not control the +scheduler, which is true for the normal OS environment. If you do control +preemption, it _can be_ possible to prevent general races - this technique is +used by frameworks such as [RTIC](https://github.com/rtic-rs/rtic). However, +actually having control over scheduling is a very uncommon case. -So it's perfectly "fine" for a Safe Rust program to get deadlocked or do -something nonsensical with incorrect synchronization. Obviously such a program -isn't very good, but Rust can only hold your hand so far. Still, a race -condition can't violate memory safety in a Rust program on its own. Only in -conjunction with some other unsafe code can a race condition actually violate -memory safety. For instance: +For this reason, it is considered "safe" for Rust to get deadlocked or do +something nonsensical with incorrect synchronization: this is known as a general +race condition or resource race. Obviously such a program isn't very good, but +Rust of course cannot prevent all logic errors. + +In any case, a race condition cannot violate memory safety in a Rust program on +its own. Only in conjunction with some other unsafe code can a race condition +actually violate memory safety. For instance, a correct program looks like this: ```rust,no_run use std::thread; @@ -58,6 +60,9 @@ thread::spawn(move || { println!("{}", data[idx.load(Ordering::SeqCst)]); ``` +We can cause a data race if we instead do the bound check in advance, and then +unsafely access the data with an unchecked value: + ```rust,no_run use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; From 360a768c6d4fc5910e9c849d793c82f9f3bf3936 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Sun, 10 Dec 2023 04:18:32 +0100 Subject: [PATCH 46/48] Improve the `PhantomData` table (#417) --- src/phantom-data.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/phantom-data.md b/src/phantom-data.md index 449d9e7..cd2428d 100644 --- a/src/phantom-data.md +++ b/src/phantom-data.md @@ -106,7 +106,14 @@ that that `Vec` _owns_ values of type `T` (more precisely: may use values of in its `Drop` implementation), and Rust will thus not allow them to _dangle_ should a `Vec` be dropped. -**Adding an extra `_owns_T: PhantomData` field is thus _superfluous_ and accomplishes nothing**. +When a type already has a `Drop impl`, **adding an extra `_owns_T: PhantomData` field +is thus _superfluous_ and accomplishes nothing**, dropck-wise (it still affects variance +and auto-traits). + + - (advanced edge case: if the type containing the `PhantomData` has no `Drop` impl at all, + but still has drop glue (by having _another_ field with drop glue), then the + dropck/`#[may_dangle]` considerations mentioned herein do apply as well: a `PhantomData` + field will then require `T` to be droppable whenever the containing type goes out of scope). ___ @@ -234,14 +241,18 @@ standard library made a utility for itself called `Unique` which: Here’s a table of all the wonderful ways `PhantomData` could be used: -| Phantom type | `'a` | `T` | `Send` | `Sync` | -|-----------------------------|-----------|-----------------------------|-----------|-----------| -| `PhantomData` | - | covariant (with drop check) | `T: Send` | `T: Sync` | -| `PhantomData<&'a T>` | covariant | covariant | `T: Sync` | `T: Sync` | -| `PhantomData<&'a mut T>` | covariant | invariant | `T: Send` | `T: Sync` | -| `PhantomData<*const T>` | - | covariant | - | - | -| `PhantomData<*mut T>` | - | invariant | - | - | -| `PhantomData` | - | contravariant | `Send` | `Sync` | -| `PhantomData T>` | - | covariant | `Send` | `Sync` | -| `PhantomData T>` | - | invariant | `Send` | `Sync` | -| `PhantomData>` | invariant | - | `Send` | - | +| Phantom type | variance of `'a` | variance of `T` | `Send`/`Sync`
(or lack thereof) | dangling `'a` or `T` in drop glue
(_e.g._, `#[may_dangle] Drop`) | +|-----------------------------|:----------------:|:-----------------:|:-----------------------------------------:|:------------------------------------------------:| +| `PhantomData` | - | **cov**ariant | inherited | disallowed ("owns `T`") | +| `PhantomData<&'a T>` | **cov**ariant | **cov**ariant | `Send + Sync`
requires
`T : Sync` | allowed | +| `PhantomData<&'a mut T>` | **cov**ariant | **inv**ariant | inherited | allowed | +| `PhantomData<*const T>` | - | **cov**ariant | `!Send + !Sync` | allowed | +| `PhantomData<*mut T>` | - | **inv**ariant | `!Send + !Sync` | allowed | +| `PhantomData` | - | **contra**variant | `Send + Sync` | allowed | +| `PhantomData T>` | - | **cov**ariant | `Send + Sync` | allowed | +| `PhantomData T>` | - | **inv**ariant | `Send + Sync` | allowed | +| `PhantomData>` | **inv**ariant | - | `Send + !Sync` | allowed | + + - Note: opting out of the `Unpin` auto-trait requires the dedicated [`PhantomPinned`] type instead. + +[`PhantomPinned`]: ../core/marker/struct.PhantomPinned.html From f6bd083c4ccfc4ce6699b8b4154e3c45c5a27a8c Mon Sep 17 00:00:00 2001 From: Eva Pace Date: Sun, 10 Dec 2023 00:19:24 -0300 Subject: [PATCH 47/48] Minor improvements to Vec (#415) --- src/vec/vec-drain.md | 20 ++++++++--------- src/vec/vec-final.md | 43 ++++++++++++++++++------------------ src/vec/vec-insert-remove.md | 5 +++-- src/vec/vec-into-iter.md | 22 +++++++++--------- src/vec/vec-layout.md | 1 - src/vec/vec-raw.md | 32 +++++++++++++-------------- 6 files changed, 59 insertions(+), 64 deletions(-) diff --git a/src/vec/vec-drain.md b/src/vec/vec-drain.md index 7a0e7f8..763c82a 100644 --- a/src/vec/vec-drain.md +++ b/src/vec/vec-drain.md @@ -93,7 +93,7 @@ impl IntoIterator for Vec { mem::forget(self); IntoIter { - iter: iter, + iter, _buf: buf, } } @@ -135,18 +135,16 @@ impl<'a, T> Drop for Drain<'a, T> { impl Vec { pub fn drain(&mut self) -> Drain { - unsafe { - let iter = RawValIter::new(&self); + let iter = unsafe { RawValIter::new(&self) }; - // this is a mem::forget safety thing. If Drain is forgotten, we just - // leak the whole Vec's contents. Also we need to do this *eventually* - // anyway, so why not do it now? - self.len = 0; + // this is a mem::forget safety thing. If Drain is forgotten, we just + // leak the whole Vec's contents. Also we need to do this *eventually* + // anyway, so why not do it now? + self.len = 0; - Drain { - iter: iter, - vec: PhantomData, - } + Drain { + iter, + vec: PhantomData, } } } diff --git a/src/vec/vec-final.md b/src/vec/vec-final.md index e680e0d..1f73036 100644 --- a/src/vec/vec-final.md +++ b/src/vec/vec-final.md @@ -127,7 +127,7 @@ impl Vec { pub fn insert(&mut self, index: usize, elem: T) { assert!(index <= self.len, "index out of bounds"); - if self.cap() == self.len { + if self.len == self.cap() { self.buf.grow(); } @@ -138,14 +138,17 @@ impl Vec { self.len - index, ); ptr::write(self.ptr().add(index), elem); - self.len += 1; } + + self.len += 1; } pub fn remove(&mut self, index: usize) -> T { assert!(index < self.len, "index out of bounds"); + + self.len -= 1; + unsafe { - self.len -= 1; let result = ptr::read(self.ptr().add(index)); ptr::copy( self.ptr().add(index + 1), @@ -157,18 +160,16 @@ impl Vec { } pub fn drain(&mut self) -> Drain { - unsafe { - let iter = RawValIter::new(&self); + let iter = unsafe { RawValIter::new(&self) }; - // this is a mem::forget safety thing. If Drain is forgotten, we just - // leak the whole Vec's contents. Also we need to do this *eventually* - // anyway, so why not do it now? - self.len = 0; + // this is a mem::forget safety thing. If Drain is forgotten, we just + // leak the whole Vec's contents. Also we need to do this *eventually* + // anyway, so why not do it now? + self.len = 0; - Drain { - iter: iter, - vec: PhantomData, - } + Drain { + iter, + vec: PhantomData, } } } @@ -197,15 +198,15 @@ impl IntoIterator for Vec { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - unsafe { - let iter = RawValIter::new(&self); - let buf = ptr::read(&self.buf); - mem::forget(self); + let (iter, buf) = unsafe { + (RawValIter::new(&self), ptr::read(&self.buf)) + }; - IntoIter { - iter: iter, - _buf: buf, - } + mem::forget(self); + + IntoIter { + iter, + _buf: buf, } } } diff --git a/src/vec/vec-insert-remove.md b/src/vec/vec-insert-remove.md index 722e20c..2acee65 100644 --- a/src/vec/vec-insert-remove.md +++ b/src/vec/vec-insert-remove.md @@ -18,7 +18,7 @@ pub fn insert(&mut self, index: usize, elem: T) { // Note: `<=` because it's valid to insert after everything // which would be equivalent to push. assert!(index <= self.len, "index out of bounds"); - if self.cap == self.len { self.grow(); } + if self.len == self.cap { self.grow(); } unsafe { // ptr::copy(src, dest, len): "copy from src to dest len elems" @@ -28,8 +28,9 @@ pub fn insert(&mut self, index: usize, elem: T) { self.len - index, ); ptr::write(self.ptr.as_ptr().add(index), elem); - self.len += 1; } + + self.len += 1; } ``` diff --git a/src/vec/vec-into-iter.md b/src/vec/vec-into-iter.md index a3a3c8c..ad22ff9 100644 --- a/src/vec/vec-into-iter.md +++ b/src/vec/vec-into-iter.md @@ -68,18 +68,16 @@ impl IntoIterator for Vec { let cap = vec.cap; let len = vec.len; - unsafe { - IntoIter { - buf: ptr, - cap: cap, - start: ptr.as_ptr(), - end: if cap == 0 { - // can't offset off this pointer, it's not allocated! - ptr.as_ptr() - } else { - ptr.as_ptr().add(len) - }, - } + IntoIter { + buf: ptr, + cap, + start: ptr.as_ptr(), + end: if cap == 0 { + // can't offset off this pointer, it's not allocated! + ptr.as_ptr() + } else { + unsafe { ptr.as_ptr().add(len) } + }, } } } diff --git a/src/vec/vec-layout.md b/src/vec/vec-layout.md index 9129952..695485f 100644 --- a/src/vec/vec-layout.md +++ b/src/vec/vec-layout.md @@ -40,7 +40,6 @@ we get the same results as using `Unique`: ```rust use std::ptr::NonNull; -use std::marker::PhantomData; pub struct Vec { ptr: NonNull, diff --git a/src/vec/vec-raw.md b/src/vec/vec-raw.md index 0bca2da..a251b4a 100644 --- a/src/vec/vec-raw.md +++ b/src/vec/vec-raw.md @@ -131,23 +131,21 @@ impl IntoIterator for Vec { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - unsafe { - // need to use ptr::read to unsafely move the buf out since it's - // not Copy, and Vec implements Drop (so we can't destructure it). - let buf = ptr::read(&self.buf); - let len = self.len; - mem::forget(self); - - IntoIter { - start: buf.ptr.as_ptr(), - end: if buf.cap == 0 { - // can't offset off of a pointer unless it's part of an allocation - buf.ptr.as_ptr() - } else { - buf.ptr.as_ptr().add(len) - }, - _buf: buf, - } + // need to use ptr::read to unsafely move the buf out since it's + // not Copy, and Vec implements Drop (so we can't destructure it). + let buf = unsafe { ptr::read(&self.buf) }; + let len = self.len; + mem::forget(self); + + IntoIter { + start: buf.ptr.as_ptr(), + end: if buf.cap == 0 { + // can't offset off of a pointer unless it's part of an allocation + buf.ptr.as_ptr() + } else { + unsafe { buf.ptr.as_ptr().add(len) } + }, + _buf: buf, } } } From 6bc2415218d4dd0cb01433d8320f5ccf79c343a1 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 2 Jan 2024 22:01:04 -0600 Subject: [PATCH 48/48] Update an example of `thread_local` to use `local_key_cell_methods` (#438) --- src/subtyping.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/subtyping.md b/src/subtyping.md index f63b532..4c45b2d 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -310,9 +310,7 @@ thread_local! { /// saves the input given into a thread local `Vec<&'static str>` fn store(input: &'static str) { - StaticVecs.with(|v| { - v.borrow_mut().push(input); - }) + StaticVecs.with_borrow_mut(|v| v.push(input)); } /// Calls the function with it's input (must have the same lifetime!) @@ -332,9 +330,8 @@ fn main() { demo(&smuggle, store); } - StaticVecs.with(|v| { - println!("{:?}", v.borrow()); // use after free 😿 - }); + // use after free 😿 + StaticVecs.with_borrow(|v| println!("{v:?}")); } ```