├── src ├── miscellanea.md ├── lt-examples.md ├── methods.md ├── dyn-any-examples.md ├── st-non-references.md ├── pitfalls.md ├── subtypes.md ├── compiler.md ├── have-no-life.md ├── st-more-invariance.md ├── c-static.md ├── pf-nll3.md ├── dyn-trait-examples.md ├── m-infect.md ├── circle.md ├── c-signatures.md ├── st-references.md ├── dyn-elision.md ├── m-no-downgrades.md ├── dyn-trait.md ├── README.md ├── m-naming.md ├── c-equality.md ├── elision.md ├── pf-meta.md ├── st-model.md ├── pf-self.md ├── fn-parameters.md ├── dont-hide.md ├── st-reborrow.md ├── pf-borrow-forever.md ├── community.md ├── lifetime-intuition.md ├── st-types.md ├── pf-dyn.md ├── st-bounds.md ├── st-invariance.md ├── m-rpitit-alikes.md ├── dyn-trait-lifetime.md ├── SUMMARY.md ├── dyn-trait-clone.md ├── dyn-trait-combining.md ├── dyn-covariance.md ├── dyn-elision-citations.md ├── dyn-trait-box-impl.md ├── dyn-trait-hash.md ├── pf-shared-nested.md ├── dyn-elision-basic.md ├── dyn-trait-erased.md ├── misc-slice.md ├── dyn-elision-advanced.md ├── dyn-trait-borrow.md ├── lt-ex-mut-slice.md ├── lifetime-analysis.md ├── dyn-hr.md ├── dyn-trait-overview.md ├── dyn-safety.md ├── dyn-trait-impls.md └── dyn-trait-eq.md ├── .gitignore ├── book.toml ├── README.md └── LICENSE /src/miscellanea.md: -------------------------------------------------------------------------------- 1 | # Miscellanea 2 | 3 | This section collects relatively small and self-contained tutorials 4 | and other tidbits. 5 | 6 | -------------------------------------------------------------------------------- /src/lt-examples.md: -------------------------------------------------------------------------------- 1 | # Illustrative examples 2 | 3 | Here we provide some examples and "recipes" to illustrate how lifetimes 4 | work (or don't work) in more practical settings. 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/methods.md: -------------------------------------------------------------------------------- 1 | # Get a feel for borrow-returning methods 2 | 3 | Here we look at how borrow-returning methods work. Our examples will consider a typical 4 | pattern: 5 | ```rust 6 | fn method(&mut self) -> &SomeReturnType {} 7 | ``` 8 | -------------------------------------------------------------------------------- /src/dyn-any-examples.md: -------------------------------------------------------------------------------- 1 | # `dyn Any` examples 2 | 3 | There are some additional examples in the [`dyn Any` section](./dyn-any.md), since they make more sense there. 4 | - [A simple `TypeMap`](./dyn-any.md#the-typemap-pattern) 5 | - [Custom downcasting](./dyn-any.md#custom-downcasting) 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | # mdbook repo 13 | *~ 14 | book/ 15 | build 16 | -------------------------------------------------------------------------------- /src/st-non-references.md: -------------------------------------------------------------------------------- 1 | # Non-references 2 | 3 | The variance of lifetime and type parameters of your own `struct`s is automatically 4 | inferred from how you use those parameters in your field. For example if you have a 5 | ```rust 6 | struct AllMine<'a, T>(&'a mut T); 7 | ``` 8 | 9 | Then `AllMine` is covariant in `'a` and invariant in `T`, just like `&'a mut T` is. 10 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | author = "QuineDot" 3 | title = "Learning Rust" 4 | description = "Advice for learning Rust" 5 | language = "en" 6 | multilingual = false 7 | src = "src" 8 | 9 | [rust] 10 | edition = "2021" 11 | 12 | [output.html] 13 | git-repository-url = "https://github.com/QuineDot/rust-learning" 14 | default-theme = "navy" 15 | site-url = "/rust-learning/" 16 | fold.enable = true 17 | -------------------------------------------------------------------------------- /src/pitfalls.md: -------------------------------------------------------------------------------- 1 | # Learn some pitfalls and anti-patterns 2 | 3 | Here we cover some pitfalls to recognize and anti-patterns to avoid. 4 | 5 | First, [read this list of common lifetime misconceptions](https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md) by @pretzelhammer. 6 | Skip or skim the parts that don't make sense to you yet, and return to it for a re-read occasionally. 7 | 8 | -------------------------------------------------------------------------------- /src/subtypes.md: -------------------------------------------------------------------------------- 1 | # Get a feel for variance, references, and reborrows 2 | 3 | [Here's some official docs on the topic of variance,](https://doc.rust-lang.org/reference/subtyping.html) 4 | but reading it may make you go cross-eyed. As an alternative, in this section I attempt to introduce some basic 5 | rules about how references work with regard to lifetimes over the course of a few layers. 6 | 7 | If it still makes you cross-eyed, just skim or skip ahead. 8 | -------------------------------------------------------------------------------- /src/compiler.md: -------------------------------------------------------------------------------- 1 | # Scrutinize compiler advice 2 | 3 | 4 | The compiler gives better errors than pretty much any other language I've used, but it still does give some poor suggestions in some cases. 5 | It's hard to turn a borrow check error into an accurate "what did the programmer mean" suggestion. 6 | So suggested bounds are an area where it can be better to take a moment to try and understand what's going on with the lifetimes, 7 | instead of just blindly applying compiler advice. 8 | 9 | I'll cover a few scenarios here. 10 | 11 | -------------------------------------------------------------------------------- /src/have-no-life.md: -------------------------------------------------------------------------------- 1 | # Prefer ownership over long-lived references 2 | 3 | A common newcomer mistake is to overuse references or other lifetime-carrying 4 | data structures by storing them in their own long-lived structures. This is 5 | often a mistake as the primary use-case of non-`'static` references is for 6 | short-term borrows. 7 | 8 | If you're trying to create a lifetime-carrying struct, stop and consider if 9 | you could use a struct with no lifetime instead, for example by replacing 10 | `&str` with `String`, or `&[T]` with `Vec`. 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/st-more-invariance.md: -------------------------------------------------------------------------------- 1 | # Invariance elsewhere 2 | 3 | While behind a `&mut` is the most common place to first encounter invariance, 4 | it's present elsewhere as well. 5 | 6 | `Cell` and `RefCell` are also invariant in `T`. 7 | 8 | Trait parameters are invariant too. As a result, lifetime-parameterized traits can be onerous to work with. 9 | 10 | Additionally, if you have a bound like `T: Trait`, `U` becomes invariant because it's a type parameter of the trait. 11 | If your `U` resolves to `&'x V`, the lifetime `'x` will be invariant too. 12 | 13 | -------------------------------------------------------------------------------- /src/c-static.md: -------------------------------------------------------------------------------- 1 | # Advice to add a static bound 2 | 3 | The compiler is gradually getting better about this, but when it suggests to use a `&'static` or that a lifetime needs to outlive `'static`, it usually *actually* means either 4 | * You're in a context where non-`'static` references and other non-`static` types aren't allowed 5 | * You should add a lifetime parameter somewhere 6 | 7 | Rather than try to cook up my own example, [I'll just link to this issue.](https://github.com/rust-lang/rust/issues/50212) 8 | Although it's closed, there's still room for improvement in some of the examples within. 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/pf-nll3.md: -------------------------------------------------------------------------------- 1 | # Conditional return of a borrow 2 | 3 | 4 | The compiler isn't perfect, and there are some things it doesn't yet accept which are in fact sound and could be accepted. 5 | Perhaps the most common one to trip on is [conditional return of a borrow,](https://github.com/rust-lang/rust/issues/51545) aka NLL Problem Case #3. 6 | There are some examples and workarounds in the issue and related issues. 7 | 8 | The plan is still to accept that pattern some day. 9 | 10 | More generally, if you run into something and don't understand why it's an error or think it should be allowed, try asking in a forum post. 11 | 12 | -------------------------------------------------------------------------------- /src/dyn-trait-examples.md: -------------------------------------------------------------------------------- 1 | # `dyn Trait` examples 2 | 3 | Here we provide some "recipes" for common `dyn Trait` implementation 4 | patterns. 5 | 6 | In the examples, we'll typically be working with `dyn Trait`, `Box`, 7 | and so on for the sake of brevity. But note that in more practical code, there 8 | is a good chance you would also need to provide implementations for 9 | `Box` or other variations across auto-traits. This 10 | may be in place of implementations for `dyn Trait` (if you always need the 11 | auto-trait bounds) or in addition to implementations for `dyn Trait` 12 | (to provide maximum flexibility). 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-learning 2 | Advice for learning Rust 3 | 4 | [Read the guide on Github Pages.](https://quinedot.github.io/rust-learning/) 5 | 6 | ## About 7 | 8 | I'm mainly active in the Rust community via [URLO.](https://users.rust-lang.org/) 9 | The first version of this guide has its roots in a post on the forum where someone 10 | asked for general advice for understanding and fixing borrow-check errors. They 11 | subsequently [asked if it could be made easier to find,](https://users.rust-lang.org/t/practical-suggestions-for-fixing-lifetime-errors/92634/17) 12 | and I created this guide repository as a result. 13 | 14 | Corrections and suggested additions are welcome, but be fore-warned that I 15 | can be very slow and sporadic to respond on GitHub. 16 | -------------------------------------------------------------------------------- /src/m-infect.md: -------------------------------------------------------------------------------- 1 | # Bound-related lifetimes "infect" each other 2 | 3 | Separating `'a` and `'b` in the last section didn't make things any more flexible in terms of `self` being borrowed. 4 | Once you declare a bound like `'a: 'b`, then the two lifetimes "infect" each other. 5 | Even though the return type had a different lifetime than the input, it was still effectively a reborrow of the input. 6 | 7 | This can actually happen between two input parameters too: if you've stated a lifetime relationship between two borrows, 8 | the compiler assumes they can observe each other in some sense. It's probably not anything you'll run into soon, 9 | but if you do, the compiler errors tend to be drop errors ("borrow might be used here, when `x` is dropped"), or 10 | sometimes read like "data flows from X into Y". 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/circle.md: -------------------------------------------------------------------------------- 1 | # Circle back 2 | 3 | Ownership, borrowing, and lifetimes is a huge topic. There's way too much in this "intro" guide alone for you to absorb everything in it at once. 4 | So occasionally circle back and revisit the common misconceptions, or the documentation on variance, or take another crack at some complicated problem you saw. 5 | Your mental model will expand over time; it's enough in the beginning to know some things exist and revisit them when you run into a wall. 6 | 7 | Moreover, Rust is practicality oriented, and the abilities of the compiler have developed organically to allow common patterns soundly and ergonomically. 8 | Which is to say that the borrow checker has a fractal surface; there's an exception to any mental model of the borrow checker. 9 | So there's always something new to learn, forget, and relearn, if you're into that. 10 | 11 | -------------------------------------------------------------------------------- /src/c-signatures.md: -------------------------------------------------------------------------------- 1 | # Advice to change function signature when aliases are involved 2 | 3 | [Here's a scenario from earlier in this guide.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ad6f395f748927ae66d06b2fd42603ea) The compiler advice is: 4 | ```rust 5 | error[E0621]: explicit lifetime required in the type of `s` 6 | --> src/lib.rs:5:9 7 | | 8 | 4 | fn new(s: &str) -> Node<'_> { 9 | | ---- help: add explicit lifetime `'a` to the type of `s`: `&'a str` 10 | 5 | Self(s) 11 | | ^^^^^^^ lifetime `'a` required 12 | ``` 13 | But [this works just as well:](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9d89786c76424d66e7eb11ca7716645b) 14 | ```diff 15 | - Self(s) 16 | + Node(s) 17 | ``` 18 | And you may get this advice when implementing a trait, where you usually *can't* change the signature. 19 | 20 | -------------------------------------------------------------------------------- /src/st-references.md: -------------------------------------------------------------------------------- 1 | # Reference lifetimes 2 | 3 | Here's something you'll utilize in Rust all the time without thinking about it: 4 | * A `&'long T` coerces to a `&'short T` 5 | * A `&'long mut T` coerces to a `&'short mut T` 6 | 7 | The technical term is "covariant (in the lifetime)" but a practical mental model is "the (outer) lifetime of references can shrink". 8 | 9 | The property holds for values whose type is a reference, but it doesn't always hold for other types. 10 | For example, we'll soon see that this property doesn't always hold for the lifetime of a reference 11 | nested within another reference. When the property doesn't hold, it's usually due to *invariance*. 12 | 13 | Even if you never really think about covariance, you'll grow an intuition for it -- so much for so 14 | that you'll eventually be surprised when you encounter invariance and the property doesn't hold. 15 | We'll look at some cases soon. 16 | -------------------------------------------------------------------------------- /src/dyn-elision.md: -------------------------------------------------------------------------------- 1 | # Elision rules 2 | 3 | The `dyn Trait` lifetime elision rules are an instance of fractal complexity 4 | in Rust. Some general guidelines will get you 95% of the way there, some 5 | advanced guidelines will get you another 4% of the way there, but the deeper 6 | you go the more niche circumstances you may run into. And unfortunately, 7 | there is no proper specification to refer to. 8 | 9 | The good news is that you can override the lifetime elision behavior by 10 | being explicit about the lifetime, which provides an escape hatch from 11 | most of the complexity. So when in doubt, be explicit! 12 | 13 | In the following subsections, we present the current behavior of the compiler 14 | in layers, to the extent we have explored them. 15 | 16 | We occasionally refer to [the reference's documentation on trait object lifetime elision](https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes). 17 | However, our layered approach differs somewhat from the reference's approach, 18 | as the reference is not completely accurate. 19 | 20 | -------------------------------------------------------------------------------- /src/m-no-downgrades.md: -------------------------------------------------------------------------------- 1 | # &mut inputs don't "downgrade" to & 2 | 3 | Still talking about this signature: 4 | ```rust 5 | fn quz(&mut self) -> &str { todo!() } 6 | ``` 7 | Newcomers often expect `self` to only be shared-borrowed after `quz` returns, because the return is a shared reference. 8 | But that's not how things work; `self` remains *exclusively* borrowed for as long as the returned `&str` is valid. 9 | 10 | I find looking at the exact return type a trap when trying to build a mental model for this pattern. 11 | The fact that the lifetimes are connected is crucial, but beyond that, instead focus on the input parameter: 12 | You *cannot call* the method *until you have created* a `&mut self` with a lifetime as long as the return type has. 13 | Once that exclusive borrow (or reborrow) is created, the exclusiveness lasts for the entirety of the lifetime. 14 | Moreover, you give the `&mut self` away by calling the method (it is not `Copy`), so you can't create any other 15 | reborrows to `self` other than through whatever the method returns to you (in this case, the `&str`). 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/dyn-trait.md: -------------------------------------------------------------------------------- 1 | # A tour of `dyn Trait` 2 | 3 | Rust's type-erasing `dyn Trait` offers a way to treat different implementors 4 | of a trait in a homogenous fashion while remaining strictly and statically 5 | (i.e. compile-time) typed. For example: if you want a `Vec` of values which 6 | implement your trait, but they might not all be the same base type, you need 7 | type erasure so that you can create a `Vec>` or similar. 8 | 9 | `dyn Trait` is also useful in some situations where generics are undesirable, 10 | or to type erase unnameable types such as closures into something you need to 11 | name (such as a field type, an associated type, or a trait method return type). 12 | 13 | There is a lot to know about when and how `dyn Trait` works or does not, and 14 | how this ties together with generics, lifetimes, and Rust's type system more 15 | generally. It is therefore not uncommon to get somewhat confused about 16 | `dyn Trait` when learning Rust. 17 | 18 | In this section we take a look at what `dyn Trait` is and is not, the limitations 19 | around using it, how it relates to generics and opaque types, and more. 20 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | {{#title Learning Rust}} 2 | 3 | # Learning Rust 4 | 5 | Welcome to my collection of resources for those learning the 6 | [Rust programming language.](https://www.rust-lang.org/) The advice in 7 | these pages is typically suitable for those with at least a beginning 8 | familiarity of Rust -- for example, those who have worked through 9 | [The Book](https://doc.rust-lang.org/book/) -- but are still experiencing 10 | the growing pains of learning Rust. 11 | 12 | ## [Practical suggestions for building intuition around borrow errors](lifetime-intuition.md) 13 | 14 | In this section we outline the basics of understanding lifetimes and borrowing for 15 | those who are struggling with understanding borrow check errors. 16 | 17 | ## [Miscellanea](./miscellanea.md) 18 | 19 | This section collects relatively small and self-contained tutorials and other tidbits. 20 | 21 | ## [A tour of `dyn Trait`](./dyn-trait.md) 22 | 23 | In this section we explore what `dyn Trait` is and is not, go over its limitations 24 | and strengths, deep dive on how lifetimes work with `dyn Trait`, provide some 25 | common `dyn Trait` recipes, and more. 26 | -------------------------------------------------------------------------------- /src/m-naming.md: -------------------------------------------------------------------------------- 1 | # When not to name lifetimes 2 | 3 | Sometimes newcomers try to solve borrow check errors by making things more generic, 4 | which often involves adding lifetimes and naming previously-elided lifetimes: 5 | ```rust 6 | # struct S; impl S { 7 | fn quz<'a: 'b, 'b>(&'a mut self) -> &'b str { todo!() } 8 | # } 9 | ``` 10 | But this doesn't actually permit more lifetimes than this: 11 | ```rust 12 | # struct S; impl S { 13 | fn quz<'b>(&'b mut self) -> &'b str { todo!() } 14 | # } 15 | ``` 16 | 17 | Because in the first example, `&'a mut self` can coerce to `&'b mut self`. 18 | And, in fact, you want it to -- because you generally don't want to exclusively borrow `self` any longer than necessary. 19 | 20 | And at this point you can instead utilize lifetime elision and stick with: 21 | ```rust 22 | # struct S; impl S { 23 | fn quz(&mut self) -> &str { todo!() } 24 | # } 25 | ``` 26 | 27 | As covariance and function lifetime elision become more intuitive, you'll build a feel for when it's 28 | pointless to name lifetimes. Adding superfluous lifetimes like in the first example tends to make 29 | understanding borrow errors harder, not easier. 30 | -------------------------------------------------------------------------------- /src/c-equality.md: -------------------------------------------------------------------------------- 1 | # Advice to add bound which implies lifetime equality 2 | 3 | The example for this one is very contrived, but [consider the output here:](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=aad18e17e4308fc4e94c5fd039e64d90) 4 | ```rust 5 | fn f<'a, 'b>(s: &'a mut &'b mut str) -> &'b str { 6 | *s 7 | } 8 | ``` 9 | ```rust 10 | = help: consider adding the following bound: `'a: 'b` 11 | ``` 12 | 13 | With the nested lifetime in the argument, there's already an implied `'b: 'a` bound. 14 | If you follow the advice and add a `'a: 'b` bound, then the two bounds together imply that `'a` and `'b` are in fact the same lifetime. 15 | More clear advice would be to use a single lifetime. Even better advice for this particular example would be to return `&'a str` instead. 16 | 17 | Another possible pitfall of blindly following this advice is ending up with something like this: 18 | ```rust 19 | impl Node<'a> { 20 | fn g<'s: 'a>(&'s mut self) { /* ... */ } 21 | ``` 22 | That's [the `&'a mut Node<'a>` anti-pattern](pf-self.md) in disguise! This will probably be unusable and hints at a deeper problem that needs solved. 23 | 24 | -------------------------------------------------------------------------------- /src/elision.md: -------------------------------------------------------------------------------- 1 | # Understand elision and get a feel for when to name lifetimes 2 | 3 | [Read the function lifetime elision rules.](https://doc.rust-lang.org/reference/lifetime-elision.html) 4 | They're intuitive for the most part. The elision rules are built around being what you need in the common case, 5 | but they're not always the solution. For example, they assume an input of `&'s self` means you mean to return a 6 | borrow of `self` or its contents with lifetime `'s`, but this is often *not* correct for lifetime-carrying 7 | data structures. 8 | 9 | If you get a lifetime error involving elided lifetimes, try giving all the lifetimes names. This can improve 10 | the compiler errors; if nothing else, you won't have to work so hard to mentally track the numbers used in 11 | errors for illustration, or what "anonymous lifetime" is being talked about. 12 | 13 | Take care to refer to the elision rules when naming all the lifetimes, or you may inadvertently change the 14 | meaning of the signature. If the error changes *drastically,* you've probably changed the meaning of the 15 | signature. 16 | 17 | Once you have a good feel for the function lifetime elision rules, you'll start developing intuition for 18 | when you need to name your lifetimes instead. 19 | -------------------------------------------------------------------------------- /src/pf-meta.md: -------------------------------------------------------------------------------- 1 | # Avoid self-referential structs 2 | 3 | By self-referential, I mean you have one field that is a reference, and that reference points to another field (or contents of a field) in the same struct. 4 | ```rust 5 | struct Snek<'a> { 6 | owned: String, 7 | // Like if you want this to point to the `owned` field 8 | borrowed: &'a str, 9 | } 10 | ``` 11 | 12 | The only safe way to construct this to be self-referential is to take a `&'a mut Snek<'a>`, get a `&'a str` to the `owned` field, and assign it to the `borrowed` field. 13 | ```rust 14 | #struct Snek<'a> { 15 | # owned: String, 16 | # // Like if you want this to point to the `owned` field 17 | # borrowed: &'a str, 18 | #} 19 | 20 | impl<'a> Snek<'a> { 21 | fn bite(&'a mut self) { 22 | self.borrowed = &self.owned; 23 | } 24 | } 25 | ``` 26 | 27 | [And as was covered before,](pf-borrow-forever.md) that's an anti-pattern because you cannot use the self-referential struct directly ever again. 28 | The only way to use it at all is via a (reborrowed) return value from the method call that required `&'a mut self`. 29 | 30 | So it's technically possible, but so restrictive it's pretty much always useless. 31 | 32 | Trying to create self-referential structs is a common newcomer misstep, and you may see the response to questions about them in the approximated form of "you can't do that in safe Rust". 33 | 34 | -------------------------------------------------------------------------------- /src/st-model.md: -------------------------------------------------------------------------------- 1 | # The seed of a mental model 2 | 3 | Some find it helpful to think of shared (`&T`) and exclusive (`&mut T`) references like so: 4 | 5 | * `&T` is a compiler-checked `RwLockReadGuard` 6 | * You can have as many of these at one time as you want 7 | * `&mut T` is a compiler-checked `RwLockWriteGuard` 8 | * You can only have one of these at one time, and when you do, you can have no `RwLockReadGuard` 9 | 10 | The exclusivity is key. 11 | 12 | `&mut T` are often called "mutable references" for obvious reasons. And following from that, `&T` 13 | are often called "immutable references". However, I find it more accurate and consistent to call 14 | `&mut T` an exclusive reference and to call `&T` a shared reference. 15 | 16 | This guide doesn't yet cover shared mutability, more commonly called interior mutability, but you'll 17 | run into the concept sometime in your Rust journey. The one thing I will mention here is that it 18 | enables mutation behind a `&T`, and thus "immutable reference" is a misleading name. 19 | 20 | There are other situations where the important quality of a `&mut T` is the exclusivity that it 21 | guarantees, and not the ability to mutate through it. If you find yourself annoyed at getting 22 | borrow errors about `&mut T` when you performed no actual mutation, it may help to instead 23 | consider `&mut T` as a directive to the compiler to ensure *exclusive* access instead of 24 | *mutable* access. 25 | -------------------------------------------------------------------------------- /src/pf-self.md: -------------------------------------------------------------------------------- 1 | # `&'a mut self` and `Self` aliasing more generally 2 | 3 | `fn foo(&'a mut self)` is a red flag -- because if that `'a` wasn't declared on the function, 4 | it's probably part of the `Self` struct. And if that's the case, this is a `&'a mut Thing<'a>` 5 | in disguise. [As discussed in the previous section,](./pf-borrow-forever.md) this will make 6 | `self` unusable afterwards, and thus is an anti-pattern. 7 | 8 | More generally, `self` types and the `Self` alias include any parameters on the type constructor post-resolution. 9 | [Which means here:](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a46b294ec613acda99767069a744c488) 10 | ```rust 11 | struct Node<'a>(&'a str); 12 | 13 | impl<'a> Node<'a> { 14 | fn new(s: &str) -> Self { 15 | Node(s) 16 | } 17 | } 18 | ``` 19 | 20 | `Self` is an alias for `Node<'a>`. It is *not* an alias for `Node<'_>`. So it means: 21 | ```rust 22 | fn new<'s>(s: &'s str) -> Node<'a> { 23 | ``` 24 | And not: 25 | ```rust 26 | fn new<'s>(s: &'s str) -> Node<'s> { 27 | ``` 28 | And you really meant to code one of these: 29 | ```rust 30 | fn new(s: &'a str) -> Self { 31 | fn new(s: &str) -> Node<'_> { 32 | ``` 33 | 34 | Similarly, using `Self` as a constructor will use the resolved type parameters. So this won't work: 35 | ```rust 36 | fn new(s: &str) -> Node<'_> { 37 | Self(s) 38 | } 39 | ``` 40 | You need 41 | ```rust 42 | fn new(s: &str) -> Node<'_> { 43 | Node(s) 44 | } 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /src/fn-parameters.md: -------------------------------------------------------------------------------- 1 | # Understand function lifetime parameters 2 | 3 | First, note that elided lifetimes in function signatures are invisible lifetime parameters on the function. 4 | ```rust 5 | fn zed(s: &str) {} 6 | ``` 7 | ```rust 8 | // same thing 9 | fn zed<'s>(s: &'s str) {} 10 | ``` 11 | When you have a lifetime parameter like this, the *caller* chooses the lifetime. 12 | But the body of your function is opaque to the caller: they can only choose lifetimes *just longer* than your function body. 13 | 14 | So when you have a lifetime parameter on your function (without any further bounds), the only things you know are 15 | * It's longer than your function body 16 | * You don't get to pick it, and it could be arbitrarily long (even `'static`) 17 | * But it could be *just barely longer* than your function body too; you have to support both cases 18 | 19 | And the main corollaries are 20 | * You can't borrow locals for a caller-chosen lifetime 21 | * You can't extend a caller-chosen lifetime to some other named lifetime in scope 22 | * Unless there's some other outlives bound that makes it possible 23 | 24 | --- 25 | 26 | Here's a couple of error examples related to function lifetime parameters: 27 | ```rust 28 | fn long_borrowing_local<'a>(name: &'a str) { 29 | let local = String::new(); 30 | let borrow: &'a str = &local; 31 | } 32 | 33 | fn borrowing_zed(name: &str) -> &str { 34 | match name.len() { 35 | 0 => "Hello, stranger!", 36 | _ => &format!("Hello, {name}!"), 37 | } 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /src/dont-hide.md: -------------------------------------------------------------------------------- 1 | # Don't hide lifetimes 2 | 3 | When you use lifetime-carrying structs (whether your own or someone else's), 4 | the Rust compiler currently let's you elide the lifetime parameter when 5 | mentioning the struct: 6 | ```rust 7 | struct Foo<'a>(&'a str); 8 | 9 | impl<'a> Foo<'a> { 10 | // No lifetime syntax needed :-( 11 | // vvv 12 | fn new(s: &str) -> Foo { 13 | Foo(s) 14 | } 15 | } 16 | ``` 17 | This can make it non-obvious that borrowing is going on, and harder to figure 18 | out where errors are coming from. To save yourself some headaches, I recommend 19 | using the `#![deny(elided_lifetimes_in_paths)]` lint: 20 | ```rust,compile_fail 21 | #![deny(elided_lifetimes_in_paths)] 22 | struct Foo<'a>(&'a str); 23 | 24 | impl<'a> Foo<'a> { 25 | // Now this is an error 26 | // vvv 27 | fn new(s: &str) -> Foo { 28 | Foo(s) 29 | } 30 | } 31 | ``` 32 | The first thing I do when taking on a borrow check error in someone else's code 33 | is to turn on this lint. If you have not enabled the lint and are getting errors 34 | in your own code, try enabling the lint. For every place that errors, take a moment 35 | to pause and consider what is going on with the lifetimes. Sometimes there's only 36 | one possibility and you will just need to make a trivial change: 37 | ```diff 38 | - fn new(s: &str) -> Foo { 39 | + fn new(s: &str) -> Foo<'_> { 40 | ``` 41 | But often, in my experience, one of the error sites will be part of the problem 42 | you're dealing with. 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/st-reborrow.md: -------------------------------------------------------------------------------- 1 | # Copy and reborrows 2 | 3 | Shared references (`&T`) implement `Copy`, which makes them very flexible. Once you have one, 4 | you can have as many as you want; once you've exposed one, you can't keep track of how many there are. 5 | 6 | Exclusive references (`&mut T`) do not implement `Copy`. 7 | Instead, you can use them ergonomically through a mechanism called *reborrowing*. For example here: 8 | 9 | ```rust 10 | fn foo<'v>(v: &'v mut Vec) { 11 | v.push(0); // line 1 12 | println!("{v:?}"); // line 2 13 | } 14 | ``` 15 | 16 | You're not moving `v: &mut Vec` when you pass it to `push` on line 1, or you couldn't print it on line 2. 17 | But you're not copying it either, because `&mut _` does not implement `Copy`. 18 | Instead `*v` is reborrowed for some shorter lifetime than `'v`, which ends on line 1. 19 | 20 | An explicit reborrow would look like this: 21 | ```rust,no_compile 22 | Vec::push(&mut *v, 0); 23 | ``` 24 | 25 | `v` can't be used while the reborrow `&mut *v` exists, but after it "expires", you can use `v` again. 26 | In this way, both `&mut` are still *exclusive* borrows. 27 | 28 | Though tragically underdocumented, reborrowing is what makes `&mut` usable; there's a lot of implicit reborrowing in Rust. 29 | Reborrowing makes `&mut T` act like the `Copy`-able `&T` in some ways. But the necessity that `&mut T` is exclusive while 30 | it exists leads to it being much less flexible. 31 | 32 | Reborrowing is a large topic on its own, but you should at least understand that it exists, and is what enables Rust to 33 | be usable and ergonomic while still enforcing memory safety. 34 | -------------------------------------------------------------------------------- /src/pf-borrow-forever.md: -------------------------------------------------------------------------------- 1 | # Borrowing something forever 2 | 3 | An anti-pattern you may run into is to create a `&'a mut Thing<'a>`. It's an anti-pattern because it translates 4 | into "take an exclusive reference of `Thing<'a>` for the entire rest of it's validity (`'a`)". Once you create 5 | the exclusive borrow, you cannot use the `Thing<'a>` ever again, except via that borrow. 6 | 7 | You can't call methods on it, you can't take another reference to it, you can't move it, you can't print it, you 8 | can't use it at all. You can't even call a non-trivial destructor on it; if you have a non-trivial destructor, 9 | your code won't compile in the presence of `&'a mut Thing<'a>`. 10 | 11 | So avoid `&'a mut Thing<'a>`. 12 | 13 | --- 14 | 15 | Examples: 16 | ```rust 17 | #[derive(Debug)] 18 | struct Node<'a>(&'a str); 19 | fn example_1<'a>(node: &'a mut Node<'a>) {} 20 | 21 | struct DroppingNode<'a>(&'a str); 22 | impl Drop for DroppingNode<'_> { fn drop(&mut self) {} } 23 | fn example_2<'a>(node: &'a mut DroppingNode<'a>) {} 24 | 25 | fn main() { 26 | let local = String::new(); 27 | 28 | let mut node_a = Node(&local); 29 | // You can do this once and it's ok... 30 | example_1(&mut node_a); 31 | 32 | let mut node_b = Node(&local); 33 | // ...but then you can't use the node directly ever again 34 | example_1(&mut node_b); 35 | println!("{node_b:?}"); 36 | 37 | let mut node_c = DroppingNode(&local); 38 | // And this doesn't work at all 39 | example_2(&mut node_c); 40 | } 41 | ``` 42 | 43 | We look at the shared version of this pattern (`&'a Thing<'a>`) [a little later.](pf-shared-nested.md) 44 | -------------------------------------------------------------------------------- /src/community.md: -------------------------------------------------------------------------------- 1 | # Keep at it and participate in the community 2 | 3 | Experience plays a very big role in understanding borrow errors and being able 4 | to figure out why some code is problematic in a reasonable amount of time. 5 | Running into borrow check errors and solving them, either on your own or by 6 | asking for help, is a part of the process of building up that intuition. 7 | 8 | I personally learned a great deal by participating on the 9 | [Rust user's forum, URLO.](https://users.rust-lang.org/) Another way you can 10 | build experience and broaden your intuition is by reading other's lifetime 11 | questions on that forum, reading the replies to those questions, and trying 12 | to solve their problems -- or even just experiment with them! -- yourself. 13 | 14 | By being an active participant, you can not only learn more, but will eventually 15 | be able to help out other users. 16 | * When you read a question and are lost: read the replies and see if you can understand them and learn more 17 | * When you have played with a question and got it to compile but aren't sure why: reply with something like 18 | "I don't know if this fixes your use case, but this compiles for me: [Playground](https://play.rust-lang.org/)" 19 | * After you've got the hang of certain common problems: "It's because of XYZ. You can do this instead: ..." 20 | 21 | 22 | Even after I got my sea-legs, some of the more advanced lifetime and borrow-checker skills 23 | I've developed came from solving other people's problems on this forum. Once you can answer 24 | their questions, you may still learn things if someone else comes along and provides a 25 | different or better solution. 26 | 27 | -------------------------------------------------------------------------------- /src/lifetime-intuition.md: -------------------------------------------------------------------------------- 1 | # Practical suggestions for building intuition around borrow errors 2 | 3 | Ownership, borrowing, and lifetimes covers a lot of ground in Rust, and thus this section is somewhat long. 4 | It is also hard to create a succinct overview of the topic, as what aspects of the topic a newcomer 5 | first encounters depends on what projects they take on in the process of learning Rust. The genesis of 6 | this guide was advice for someone who picked a zero-copy regular expression crate as their learning project, 7 | and as such they ran into a lot of lifetime issues off the bat. Someone who chose a web framework would be 8 | more likely to run into issues around `Arc` and `Mutex`, those who start with `async` projects will likely 9 | run into many `async`-specific errors, and so on. 10 | 11 | Despite the length of this section, there are entire areas it doesn't yet touch on, such as destructor 12 | mechanics, shared ownership and shared mutability, and so on. 13 | 14 | Even so, it's not expected that anyone will absorb everything in this guide all at once. Instead, 15 | it's intended to be a broad introduction to the topic. Skim it on your first pass and take time on 16 | the parts that seem relevant, or use them as pointers to look up more in-depth documentation on your 17 | own. Hopefully you will get a feel for what kind errors can arise even if you haven't ran into them 18 | yourself yet, so that you're not completely baffled when they do arise. 19 | 20 | In general, your mental model of ownership and borrowing will go through a few stages of evolution. 21 | It will pay off to return to any given area of the topic with fresh eyes every once in awhile. 22 | -------------------------------------------------------------------------------- /src/st-types.md: -------------------------------------------------------------------------------- 1 | # Reference types 2 | 3 | Let's open with a question: is `&str` a type? 4 | 5 | When not being pedantic or formal, pretty much everyone will say yes, `&str` is a type. 6 | However, it is technically a *type constructor* which is parameterized with a generic 7 | lifetime parameter. So `&str` isn't technically a type, `&'a str` for some concrete 8 | lifetime is a type. `&'a str` and `&'b str` are not the same type, unless `'a == 'b`. 9 | 10 | Similarly, `Vec` for a generic `T` is a type constructor, but `Vec` is a type. 11 | `Vec` and `Vec` are not the same type, unless `T == U`. 12 | 13 | By "concrete lifetime", I mean some compile-time determined lifetime. The exact 14 | definition of "lifetime" is surprisingly complicated and beyond the scope of this 15 | guide, but here are a few examples of `&str`s and their concrete types. 16 | 17 | ```rust 18 | // The exact lifetime of `'a` is determined at each call site. We'll explore 19 | // what this means in more depth later. 20 | // 21 | // The lifetime of `b` works the same, we just didn't give it a name. 22 | fn example<'a>(a: &'a str, b: &str) { 23 | // Literal strings are `&'static str` 24 | let s = "literal"; 25 | 26 | // The lifetime of local borrows are determined by compiler analysis 27 | // and have no names (but it's still a single lifetime). 28 | let local = String::new(); 29 | let borrow = local.as_str(); 30 | 31 | // These are the same and they just tell the compiler to infer the 32 | // lifetime. In this small example that means the same thing as not 33 | // having a type annotation at all. 34 | let borrow: &str = local.as_str(); 35 | let borrow: &'_ str = local.as_str(); 36 | } 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /src/pf-dyn.md: -------------------------------------------------------------------------------- 1 | # `dyn Trait` lifetimes and `Box` 2 | 3 | Every trait object (`dyn Trait`) has an elide-able lifetime with it's own defaults when completely elided. 4 | This default is stronger than the normal function signature elision rules. 5 | The most common way to run into a lifetime error about this is with `Box` in your function signatures, structs, and type aliases, where it means `Box`. 6 | 7 | Often the error indicates that non-`'static` references/types aren't allowed in that context, 8 | but sometimes it means that you should add an explicit lifetime, like `Box` or `Box`. 9 | 10 | The latter will act like "normal" lifetime elision; for example, it will introduce a new anonymous lifetime 11 | parameter as a function input parameter, or use the `&self` lifetime in return position. 12 | 13 | The reason the lifetime exists is that coercing values to `dyn Trait` erases their base type, including any 14 | lifetimes that it may contain. But those lifetimes have to be tracked by the compiler somehow to ensure 15 | memory safety. The `dyn Trait` lifetime represents the maximum lifetime the erased type is valid for. 16 | 17 | --- 18 | 19 | Some short examples: 20 | ```rust 21 | trait Trait {} 22 | 23 | // The return is `Box` and this errors as there 24 | // needs to be a bound requiring `T` to be `'static`, or the return 25 | // type needs to be more flexible 26 | fn one(t: T) -> Box { 27 | Box::new(t) 28 | } 29 | 30 | // This works as we've added the bound 31 | fn two(t: T) -> Box { 32 | Box::new(t) 33 | } 34 | 35 | // This works as we've made the return type more flexible. We still 36 | // have to add a lifetime bound. 37 | fn three<'a, T: Trait + 'a>(t: T) -> Box { 38 | Box::new(t) 39 | } 40 | ``` 41 | 42 | For a more in-depth exploration, 43 | [see this section of the `dyn Trait` tour.](./dyn-elision.md) 44 | -------------------------------------------------------------------------------- /src/st-bounds.md: -------------------------------------------------------------------------------- 1 | # Lifetime bounds 2 | 3 | Here's a brief introduction to the lifetime bounds you may see on `fn` declarations and `impl` blocks. 4 | 5 | ## Bounds between lifetimes 6 | 7 | A `'a: 'b` bound means, roughly speaking, `'long: 'short`. 8 | It's often read as "`'a` outlives `'b`" and it sometimes called an "outlives bound" or "outlives relation". 9 | 10 | I personally also like to read it as "`'a` is valid for (at least) `'b`". 11 | 12 | Note that `'a` may be the same as `'b`, it does not have to be strictly longer despite the "outlives" 13 | terminology. It is analogous to `>=` in this respect. Therefore, in this example: 14 | ```rust 15 | fn example<'a: 'b, 'b: 'a>(a: &'a str, b: &'b str) {} 16 | ``` 17 | 18 | `'a` and `'b` must actually be the same lifetime. 19 | 20 | When you have a function argument with a nested reference such as `&'b Foo<'a>`, a `'a: 'b` bound is inferred. 21 | 22 | ## Bounds between (generic) types and lifetimes 23 | 24 | A `T: 'a` means that a `&'a T` would not be instantly undefined behavior. In other words, it 25 | means that if the type `T` contains any references or other lifetimes, they must be at least 26 | as long as `'a`. 27 | 28 | You can also read these as "(the type) `T` is valid for `'a`". 29 | 30 | Note that this has nothing to do with the liveness scope or drop scope of a *value* of type `T`! 31 | In particular the most common bound of this form is `T: 'static`. 32 | 33 | This does not mean the value of type `T` must last for your entire program! It just means that 34 | the type `T` has no non-`'static` lifetimes. `String: 'static` for example, but this doesn't 35 | mean that you don't drop `String`s. 36 | 37 | ## Liveness scopes of values 38 | 39 | For the above reasons, I prefer to never refer to the liveness or drop scope of a value as 40 | the value's "lifetime". Although there is a connection between the liveness scope of values 41 | and lifetimes of references you take to it, conflating the two concepts can lead to confusion. 42 | 43 | That said, not everyone follows this convention, so you may see the liveness scope of a value 44 | referred to as the values "lifetime". So the distinction is something to just generally be 45 | aware of. 46 | -------------------------------------------------------------------------------- /src/st-invariance.md: -------------------------------------------------------------------------------- 1 | # Nested borrows and invariance 2 | 3 | Now let's consider nested references: 4 | * A `&'medium &'long U` coerces to a `&'short &'short U` 5 | * A `&'medium mut &'long mut U` coerces to a `&'short mut &'long mut U`... 6 | * ...but *not* to a `&'short mut &'short mut U` 7 | 8 | We say that `&mut T` is *invariant* in `T`, which means any lifetimes in `T` cannot change 9 | (grow or shrink) at all. In the example, `T` is `&'long mut U`, and the `'long` cannot be changed. 10 | 11 | Why not? Consider this: 12 | ```rust 13 | fn bar(v: &mut Vec<&'static str>) { 14 | let w: &mut Vec<&'_ str> = v; // call the lifetime 'w 15 | let local = "Gottem".to_string(); 16 | w.push(&*local); 17 | } // `local` drops 18 | ``` 19 | If `'w` was allowed to be shorter than `'static`, we'd end up with a dangling reference in `*v` after `bar` returns. 20 | 21 | You will inevitably end up with a feel for covariance from using references with their flexible outer lifetimes, 22 | but eventually hit a use case where invariance matters and causes some borrow check errors, because it's (necessarily) so much less flexible. 23 | It's just part of the Rust learning experience. 24 | 25 | --- 26 | 27 | Let's look at one more property of nested references you may run into: 28 | * You can get a `&'long U` from a `&'short &'long U`: 29 | * Just copy it out! 30 | * But you cannot get a `&'long mut U` through dereferencing a `&'short mut &'long mut U`. 31 | * You can only reborrow a `&'short mut U`. 32 | * Obtaining a `&'long mut U` is sometimes possible by swapping or 33 | temporarily moving out of the inner reference, for example with [`mem::swap`](https://doc.rust-lang.org/stable/std/mem/fn.swap.html), 34 | [`mem::replace`](https://doc.rust-lang.org/stable/std/mem/fn.replace.html), 35 | or [`replace_with`](https://docs.rs/replace_with/). See the [mutable slice iterator example](./lt-ex-mut-slice.md). 36 | 37 | The reason is again to prevent memory unsafety. 38 | 39 | Additionally, 40 | * You cannot get a `&'long U` or *any* `&mut U` from a `&'short &'long mut U` 41 | * You can only reborrow a `&'short U` 42 | 43 | Recall that once a shared reference exist, any number of copies of it could 44 | simultaneously exist. Therefore, so long as the outer shared reference exists 45 | (and could be used to observe `U`), the inner `&mut` must not be usable in a 46 | mutable or otherwise exclusive fashion. 47 | 48 | And once the outer reference expires, the inner `&mut` is active and must 49 | again be exclusive, so it must not be possible to obtain a `&'long U` either. 50 | -------------------------------------------------------------------------------- /src/m-rpitit-alikes.md: -------------------------------------------------------------------------------- 1 | # `async` and returning `impl Trait` 2 | 3 | The return type of an `async` function captures all of its generic parameters, 4 | including any lifetimes. So here: 5 | ```rust 6 | async fn example(v: &mut Vec) -> String { 7 | "Hi :-)".to_string() 8 | } 9 | ``` 10 | The future returned by the `async fn` implicitly reborrows the `v` input, and 11 | "carries" the same lifetime, just like the other examples we saw. 12 | 13 | The same is true when you use `return`-position `impl Trait` (RPIT) in traits (RPITIT): 14 | ```rust,compile_fail 15 | # struct MyStruct {} 16 | trait StringIter { 17 | fn iter(&self) -> impl Iterator; 18 | } 19 | 20 | impl StringIter for MyStruct { 21 | fn iter(&self) -> impl Iterator { 22 | ["Hi :-)"].into_iter().map(ToString::to_string) 23 | } 24 | } 25 | 26 | // Fails to compile as `iter` borrows `my` 27 | fn example(my: MyStruct) { 28 | let iter = my.iter(); 29 | let _move_my = my; 30 | for _ in iter {} 31 | } 32 | ``` 33 | 34 | `self` will remained borrowed here for as long as the iterator is alive. Note that 35 | this is true even if it's not required by the body! The implicit lifetime capture 36 | is considered part of the API contract. 37 | 38 | For both of these cases, all generic *types* are also captured. 39 | 40 | --- 41 | 42 | Not that RPIT *outside* of traits does *not* implicitly capture lifetimes! At least, 43 | not as of this writing -- the plan is that RPIT outside of traits will act like RPITIT 44 | and implicitly capture all lifetimes in edition 2024 and beyond. But for now, they 45 | only implicitly capture type parameters. 46 | 47 | ```rust 48 | # use std::fmt::Display; 49 | // Required: edition 2021 or before 50 | fn no_capture(s: &str) -> impl Display { 51 | s.to_string() 52 | } 53 | 54 | // This wouldn't compile if `no_capture` reborrowed `*s` 55 | fn check() { 56 | let mut s = "before".to_string(); 57 | let d = no_capture(&s); 58 | s ="after".to_string(); 59 | println!("{d}"); 60 | } 61 | ``` 62 | 63 | ```rust 64 | # use std::fmt::Display; 65 | // This fails on edition 2021 or before, because it tries to 66 | // return a reborrow of `*s`, but that requires capturing the lifetime 67 | fn no_capture(s: &str) -> impl Display { 68 | s 69 | } 70 | ``` 71 | 72 | ```rust 73 | # use std::fmt::Display; 74 | // This allows it to work again (but `+ '_` is too restrictive 75 | // for every situation, which is part of why edition 2021 will 76 | // change the behavior of RPIT outside of traits) 77 | // 78 | // vvvv 79 | fn no_capture(s: &str) -> impl Display + '_ { 80 | s 81 | } 82 | ``` 83 | 84 | --- 85 | 86 | A lot can be said about `async` and RPITs; more than can be covered here. But their 87 | implicit capturing nature is something to be aware of, given how invisible it is. 88 | 89 | -------------------------------------------------------------------------------- /src/dyn-trait-lifetime.md: -------------------------------------------------------------------------------- 1 | # `dyn Trait` lifetimes 2 | 3 | As mentioned before, every `dyn Trait` has a "trait object lifetime". Even though 4 | it is often elided, the lifetime is always present. 5 | 6 | The lifetime is necessary as types which implement `Trait` may not be valid everywhere. 7 | For example, `&'s String` implements `Display` for any lifetime `'s`. If you type 8 | erase a `&'s String` into a `dyn Display`, Rust needs to keep track of that lifetime 9 | so you don't try to print the value after the reference becomes invalid. 10 | 11 | So you can coerce `&'s String` to `dyn Display + 's`, but not `dyn Display + 'static`. 12 | 13 | Let's look at a couple examples: 14 | ```rust,compile_fail 15 | # use core::fmt::Display; 16 | fn fails() -> Box { 17 | let local = String::new(); 18 | // This reference cannot be longer than the function body 19 | let borrow = &local; 20 | // We can coerce it to `dyn Display`... 21 | let bx: Box = Box::new(borrow); 22 | // But the lifetime cannot be `'static`, so this is an error 23 | bx 24 | } 25 | ``` 26 | ```rust 27 | # use core::fmt::Display; 28 | // This is fine as per the function lifetime elision rules, the lifetime of the 29 | // `dyn Display + '_` is the same as the lifetime of the `&String`, and we know 30 | // the reference is valid for that long or it wouldn't be possible to call the 31 | // function. 32 | fn works(s: &String) -> Box { 33 | Box::new(s) 34 | } 35 | ``` 36 | 37 | ## When multiple lifetimes are involved 38 | 39 | Let's try another example, with a `struct` that has more complicated lifetimes. 40 | ```rust 41 | trait Trait {} 42 | 43 | // We're using `*mut` to make the lifetimes invariant 44 | struct MultiRef<'a, 'b>(*mut &'a str, *mut &'b str); 45 | 46 | impl Trait for MultiRef<'_, '_> {} 47 | 48 | fn foo<'a, 'b>(mr: MultiRef<'a, 'b>) { 49 | let _: Box = Box::new(mr); 50 | } 51 | ``` 52 | 53 | This compiles, but there's nothing preventing either `'a` from being longer than `'b`, 54 | or `'b` from being longer than `'a`. So what's the lifetime of the `dyn Trait`? It 55 | can't be either `'a` or `'b`: 56 | ```rust,compile_fail 57 | # trait Trait {} 58 | # #[derive(Copy, Clone)] struct MultiRef<'a, 'b>(*mut &'a str, *mut &'b str); 59 | # impl Trait for MultiRef<'_, '_> {} 60 | // These both fail 61 | fn foo<'a, 'b>(mr: MultiRef<'a, 'b>) { 62 | let _: Box = Box::new(mr); 63 | let _: Box = Box::new(mr); 64 | } 65 | ``` 66 | 67 | In this case, the compiler computes some lifetime, let's call it `'c`, 68 | such that `'a` and `'b` are both valid for the entirety of `'c`. 69 | 70 | That is, `'c` is contained in an intersection of `'a` and `'b`. 71 | 72 | Any lifetime for which both `'a` and `'b` are valid over will do: 73 | ```rust 74 | # trait Trait {} 75 | # struct MultiRef<'a, 'b>(*mut &'a str, *mut &'b str); 76 | # impl Trait for MultiRef<'_, '_> {} 77 | // `'c` must be within the intersection of `'a` and `'b` 78 | fn foo<'a: 'c, 'b: 'c, 'c>(mr: MultiRef<'a, 'b>) { 79 | let _: Box = Box::new(mr); 80 | } 81 | ``` 82 | 83 | Note that this is not the same as `'a + 'b` -- that is the *union* 84 | of `'a` and `'b`. Unfortunately, there is no compact syntax 85 | for the intersection of `'a` and `'b`. 86 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Quine Zine: Learning Rust 2 | 3 | [Introduction](README.md) 4 | 5 | --- 6 | 7 | # Practical suggestions for building intuition around borrow errors 8 | 9 | - [Sectional introduction](lifetime-intuition.md) 10 | - [Keep at it and participate in the community](community.md) 11 | - [Prefer ownership over long-lived references](have-no-life.md) 12 | - [Don't hide lifetimes](dont-hide.md) 13 | - [Understand elision and get a feel for when to name lifetimes](elision.md) 14 | - [Get a feel for variance, references, and reborrows](subtypes.md) 15 | - [The seed of a mental model](st-model.md) 16 | - [Reference types](st-types.md) 17 | - [Lifetime bounds](st-bounds.md) 18 | - [Reference lifetimes](st-references.md) 19 | - [Copy and reborrows](st-reborrow.md) 20 | - [Nested borrows and invariance](st-invariance.md) 21 | - [Invariance elsewhere](st-more-invariance.md) 22 | - [Non-references](st-non-references.md) 23 | - [Get a feel for borrow-returning methods](methods.md) 24 | - [When not to name lifetimes](m-naming.md) 25 | - [Bound-related lifetimes "infect" each other](m-infect.md) 26 | - [`&mut` inputs don't "downgrade" to `&`](m-no-downgrades.md) 27 | - [`async` and returning `impl Trait`](m-rpitit-alikes.md) 28 | - [Understand function lifetime parameters](fn-parameters.md) 29 | - [Understand borrows within a function](lifetime-analysis.md) 30 | - [Learn some pitfalls and antipatterns](pitfalls.md) 31 | - [`dyn Trait` lifetimes and `Box`](pf-dyn.md) 32 | - [Conditional return of a borrow](pf-nll3.md) 33 | - [Borrowing something forever](pf-borrow-forever.md) 34 | - [`&'a mut self` and `Self` aliasing more generally](pf-self.md) 35 | - [Avoid self-referential structs](pf-meta.md) 36 | - [`&'a Struct<'a>` and covariance](pf-shared-nested.md) 37 | - [Scrutinize compiler advice](compiler.md) 38 | - [Advice to change function signature when aliases are involved](c-signatures.md) 39 | - [Advice to add bound which implies lifetime equality](c-equality.md) 40 | - [Advice to add a static bound](c-static.md) 41 | - [Illustrative examples](lt-examples.md) 42 | - [Mutable slice iterator](lt-ex-mut-slice.md) 43 | - [Circle back](circle.md) 44 | 45 | --- 46 | 47 | # Miscellanea 48 | 49 | - [Sectional introduction](miscellanea.md) 50 | - [Slice layout](misc-slice.md) 51 | - [Default parameter mechanics](misc-default-param.md) 52 | 53 | --- 54 | 55 | # A tour of `dyn Trait` 56 | 57 | - [Sectional introduction](dyn-trait.md) 58 | - [`dyn Trait` overview](dyn-trait-overview.md) 59 | - [`dyn Trait` implementations](dyn-trait-impls.md) 60 | - [`dyn Trait` coercions](dyn-trait-coercions.md) 61 | - [`dyn` safety (object safety)](dyn-safety.md) 62 | - [`dyn Trait` lifetimes](dyn-trait-lifetime.md) 63 | - [Variance](dyn-covariance.md) 64 | - [Higher-ranked types](dyn-hr.md) 65 | - [Elision rules](dyn-elision.md) 66 | - [Basic guidelines](dyn-elision-basic.md) 67 | - [Advanced guidelines](dyn-elision-advanced.md) 68 | - [Trait bound interactions](dyn-elision-trait-bounds.md) 69 | - [Citations](dyn-elision-citations.md) 70 | - [`dyn Trait` vs. alternatives](dyn-trait-vs.md) 71 | - [`dyn Trait` examples](dyn-trait-examples.md) 72 | - [Combining traits](dyn-trait-combining.md) 73 | - [`impl Trait for Box`](dyn-trait-box-impl.md) 74 | - [Cloneable `Box`](dyn-trait-clone.md) 75 | - [`dyn PartialEq`](dyn-trait-eq.md) 76 | - [Generalizing borrows](dyn-trait-borrow.md) 77 | - [Erased traits](dyn-trait-erased.md) 78 | - [Hashable `Box`](dyn-trait-hash.md) 79 | - [`dyn Any` examples](dyn-any-examples.md) 80 | - [`dyn Any`](dyn-any.md) 81 | -------------------------------------------------------------------------------- /src/dyn-trait-clone.md: -------------------------------------------------------------------------------- 1 | # Clonable `Box` 2 | 3 | What can you do if you want a `Box` that you can clone? 4 | You can't have `Clone` as a [supertrait,](dyn-trait-combining.md) 5 | because `Clone` requires `Sized` and that will make `Trait` be 6 | [non-object-safe.](dyn-safety.md) 7 | 8 | You might be tempted to do this: 9 | 10 | ```rust 11 | trait Trait { 12 | fn dyn_clone(&self) -> Self where Self: Sized; 13 | } 14 | ``` 15 | 16 | But then `dyn Trait` won't have the method available, and that will 17 | [be a barrier to implementing `Trait` for `Box`.](dyn-trait-box-impl.md) 18 | 19 | But hey, you know what? Since this only really makes sense for 20 | base types that implement `Clone`, we don't need a method that returns 21 | `Self`. The base types already have that, it's called `clone`. 22 | 23 | What we ultimately want is to get a `Box` instead, like so: 24 | ```rust 25 | trait Trait { 26 | fn dyn_clone<'s>(&self) -> Box where Self: 's; 27 | } 28 | 29 | // example implementor 30 | impl Trait for String { 31 | fn dyn_clone<'s>(&self) -> Box where Self: 's { 32 | Box::new(self.clone()) 33 | } 34 | } 35 | ``` 36 | 37 | If we omit all the lifetime stuff, it only works with `Self: 'static` 38 | due to the [default `'static` lifetime.](dyn-elision-basic.md) And 39 | sometimes, that's perfectly ok! But we'll stick with the more general 40 | version for this example. 41 | 42 | The example implementation will make `dyn Trait` do the right thing 43 | (clone the underlying base type via its implementation). We can't have 44 | a default body though, because the implementation requires `Clone` 45 | and `Sized`, which again, we don't want as bounds. 46 | 47 | But this is exactly the situation we had when we looked at 48 | [manual supertrait upcasting](./dyn-trait-combining.md#manual-supertrait-upcasting) 49 | and the [`self` receiver helper](./dyn-trait-box-impl.md) 50 | in previous examples. The same pattern 51 | will work here: move the method to a helper supertrait and supply 52 | a blanket implementation for those cases where it makes sense. 53 | 54 | ```rust 55 | trait DynClone { 56 | fn dyn_clone<'s>(&self) -> Box where Self: 's; 57 | } 58 | 59 | impl DynClone for T { 60 | fn dyn_clone<'s>(&self) -> Box where Self: 's { 61 | Box::new(self.clone()) 62 | } 63 | } 64 | 65 | trait Trait: DynClone {} 66 | ``` 67 | 68 | Now we're ready for `Box`. 69 | ```rust 70 | #trait Trait: DynClone {} 71 | #trait DynClone { 72 | # fn dyn_clone<'s>(&self) -> Box where Self: 's; 73 | #} 74 | #impl DynClone for T { 75 | # fn dyn_clone<'s>(&self) -> Box where Self: 's { 76 | # Box::new(self.clone()) 77 | # } 78 | #} 79 | impl Trait for Box {} 80 | 81 | impl Clone for Box { 82 | fn clone(&self) -> Self { 83 | // Important! "recursive trait implementation" style 84 | (**self).dyn_clone() 85 | } 86 | } 87 | ``` 88 | 89 | It's important that we called `::dyn_clone`! Our 90 | blanket implementation of `DynClone` was bounded on `Clone + Trait`, but 91 | now we have implemented both of those for `Box`. If we 92 | had just called `self.dyn_clone()`, the call graph would go like so: 93 | ```rust,ignore 94 | as Clone >::clone() 95 | as DynClone>::dyn_clone() 96 | as Clone >::clone() 97 | as DynClone>::dyn_clone() 98 | as Clone >::clone() 99 | as DynClone>::dyn_clone() 100 | ... 101 | ``` 102 | 103 | Yep, infinite recursion. Just like when implementing `Trait for Box`, 104 | we need to call the `dyn Trait` method directly to avoid this. 105 | 106 | --- 107 | 108 | There is also a crate for this use case: [the `dyn-clone` crate.](https://crates.io/crates/dyn_clone) 109 | 110 | A comparison with the crate is beyond the scope of this guide for now. 111 | -------------------------------------------------------------------------------- /src/dyn-trait-combining.md: -------------------------------------------------------------------------------- 1 | # Combining traits 2 | 3 | Rust has no support for directly combining multiple non-auto traits 4 | into one `dyn Trait1 + Trait2`: 5 | ```rust,compile_fail 6 | trait Foo { fn foo(&self) {} } 7 | trait Bar { fn bar(&self) {} } 8 | 9 | // Fails 10 | let _: Box = todo!(); 11 | ``` 12 | 13 | However, the methods of a supertrait are available to the subtrait. 14 | What's a supertrait? A supertrait is a trait bound on `Self` in the 15 | definition of the subtrait, like so: 16 | ```rust 17 | # trait Foo { fn foo(&self) {} } 18 | # trait Bar { fn bar(&self) {} } 19 | trait Subtrait: Foo 20 | // ^^^^^^^^^^^^^ A supertrait bound 21 | where 22 | Self: Bar, 23 | // ^^^^^^^^^ Another one 24 | {} 25 | ``` 26 | The supertrait bound is implied everywhere the subtrait bound is 27 | present, and the methods of the supertrait are always available on 28 | implementors of the subtrait. 29 | 30 | Using these relationships, you can support something analogous to 31 | `dyn Foo + Bar` by using `dyn Subtrait`. 32 | 33 | ```rust 34 | # trait Foo { fn foo(&self) {} } 35 | # trait Bar { fn bar(&self) {} } 36 | # impl Foo for () {} 37 | # impl Bar for () {} 38 | trait Subtrait: Foo + Bar {} 39 | 40 | // Blanket implement for everything that meets the bounds... 41 | // ...including non-`Sized` types 42 | impl Subtrait for T where T: Foo + Bar {} 43 | 44 | fn main() { 45 | let quz: &dyn Subtrait = &(); 46 | quz.foo(); 47 | quz.bar(); 48 | } 49 | ``` 50 | 51 | Note that despite the terminology, there is no sub/super *type* relationship 52 | between sub/super traits, between `dyn SubTrait` and `dyn SuperTrait`, 53 | between implementors of said traits, et cetera. 54 | [Traits are not about sub/super typing.](./dyn-trait-overview.md#dyn-trait-is-not-a-supertype) 55 | 56 | ## Manual supertrait upcasting 57 | 58 | [Supertrait upcasting is planned, but not yet stable.](./dyn-trait-coercions.md#supertrait-upcasting) 59 | Until stabilized, if you need to cast something like `dyn Subtrait` to `dyn Foo`, you 60 | have to supply the implementation yourself. 61 | 62 | For a start, we could build it into our traits like so: 63 | ```rust 64 | trait Foo { 65 | fn foo(&self) {} 66 | fn as_dyn_foo(&self) -> &dyn Foo; 67 | } 68 | ``` 69 | 70 | But we can't supply a default function body, as `Self: Sized` is required to perform 71 | the type erasing cast to `dyn Foo`. We don't want that restriction or the method 72 | won't be available on `dyn Supertrait`, which is not `Sized`. 73 | 74 | Instead we can separate out the method and supply an implementation for all `Sized` 75 | types, via another supertrait: 76 | ```rust 77 | trait AsDynFoo { 78 | fn as_dyn_foo(&self) -> &dyn Foo; 79 | } 80 | 81 | trait Foo: AsDynFoo { fn foo(&self) {} } 82 | ``` 83 | And then supply the implementation for all `Sized + Foo` types: 84 | ```rust 85 | # trait AsDynFoo { fn as_dyn_foo(&self) -> &dyn Foo; } 86 | # trait Foo: AsDynFoo { fn foo(&self) {} } 87 | impl AsDynFoo for T { 88 | fn as_dyn_foo(&self) -> &dyn Foo { 89 | self 90 | } 91 | } 92 | ``` 93 | The compiler will supply the implementation for both `dyn AsDynFoo` and `dyn Foo`. 94 | 95 | When we put this altogether with the `Subtrait` from above, we can now utilize 96 | an explicit version of supertrait upcasting: 97 | ```rust 98 | trait Foo: AsDynFoo { fn foo(&self) {} } 99 | trait Bar: AsDynBar { fn bar(&self) {} } 100 | 101 | impl Foo for () {} 102 | impl Bar for () {} 103 | 104 | trait AsDynFoo { fn as_dyn_foo(&self) -> &dyn Foo; } 105 | trait AsDynBar { fn as_dyn_bar(&self) -> &dyn Bar; } 106 | impl AsDynFoo for T { fn as_dyn_foo(&self) -> &dyn Foo { self } } 107 | impl AsDynBar for T { fn as_dyn_bar(&self) -> &dyn Bar { self } } 108 | 109 | trait Subtrait: Foo + Bar {} 110 | impl Subtrait for T where T: Foo + Bar {} 111 | 112 | fn main() { 113 | let quz: &dyn Subtrait = &(); 114 | quz.foo(); 115 | quz.bar(); 116 | let _: &dyn Foo = quz.as_dyn_foo(); 117 | let _: &dyn Bar = quz.as_dyn_bar(); 118 | } 119 | ``` 120 | -------------------------------------------------------------------------------- /src/dyn-covariance.md: -------------------------------------------------------------------------------- 1 | # Variance 2 | 3 | The `dyn Trait` lifetime is covariant, like the outer lifetime of a 4 | reference. This means that whenever it is in a covariant type position, 5 | longer lifetimes can be coerced into shorter lifetimes. 6 | ```rust 7 | # trait Trait {} 8 | fn why_be_static<'a>(bx: Box) -> Box { 9 | bx 10 | } 11 | ``` 12 | The trait object with the longer lifetime is a subtype of the trait object with 13 | the shorter lifetime, so this is a form of supertype coercion. [In the next 14 | section,](./dyn-hr.md) we'll look at another form of trait object subtyping. 15 | 16 | The idea behind *why* trait object lifetimes are covariant is that the lifetime 17 | represents the region where it is still valid to call methods on the trait object. 18 | Since it's valid to call methods anywhere in that region, it's also valid to restrict 19 | the region to some subset of itself -- i.e. to coerce the lifetime to be shorter. 20 | 21 | However, it turns out that the `dyn Trait` lifetime is even more flexible than 22 | your typical covariant lifetime. 23 | 24 | ## Unsizing coercions in invariant context 25 | 26 | [Earlier we noted that](./dyn-trait-coercions.md#the-reflexive-case) 27 | you can cast a `dyn Trait + 'a` to a `dyn Trait + 'b`, where `'a: 'b`. 28 | Well, isn't that just covariance? Not quite -- when we noted this before, 29 | we were talking about an *unsizing coercion* between two `dyn Trait + '_`. 30 | 31 | And *that coercion can take place even in invariant position.* That means 32 | that the `dyn Trait` lifetime can act in a covariant-like fashion *even in 33 | invariant contexts!* 34 | 35 | For example, this compiles, even though the `dyn Trait` is behind a `&mut`: 36 | ```rust 37 | # trait Trait {} 38 | fn invariant_coercion<'m, 'long: 'short, 'short>( 39 | arg: &'m mut (dyn Trait + 'long) 40 | ) -> 41 | &'m mut (dyn Trait + 'short) 42 | { 43 | arg 44 | } 45 | ``` 46 | 47 | But as there are [no nested unsizing coercions,](./dyn-trait-coercions.md#no-nested-coercions) 48 | this version does not compile: 49 | ```rust,compile_fail 50 | # use std::cell::Cell; 51 | # trait Trait {} 52 | // Fails: `Cell` is invariant in `T` and the `dyn Trait` is nested 53 | fn foo<'l: 's, 's>(v: Cell>>) -> Cell>> { 54 | v 55 | } 56 | ``` 57 | 58 | Because this is an unsizing coercion and not a subtyping coercion, there 59 | may be situations where you must make the coercion explicitly, for example 60 | with a cast. 61 | ```rust 62 | # trait Trait {} 63 | // This fails without the `as _` cast. 64 | fn foo<'a>(arg: &'a mut Box) -> Option<&'a mut (dyn Trait + 'a)> { 65 | true.then(move || arg.as_mut() as _) 66 | } 67 | ``` 68 | 69 | ### Why this is actually a critical feature 70 | 71 | We'll examine elided lifetime in depth [soon,](./dyn-elision.md) but let us 72 | note here how this "ultra-covariance" is very important for making common patterns 73 | usably ergonomic. 74 | 75 | The signatures of `foo` and `bar` are effectively the same in the following example: 76 | ```rust 77 | # trait Trait {} 78 | fn foo(d: &mut dyn Trait) {} 79 | 80 | fn bar<'a>(d: &'a mut (dyn Trait + 'a)) { 81 | foo(d); 82 | foo(d); 83 | } 84 | ``` 85 | 86 | We can call `foo` multiple times from `bar` by [reborrowing](./st-reborrow.md) 87 | the `&'a mut dyn Trait` for shorter than `'a`. But because the trait object 88 | lifetime must match the outer `&mut` lifetime in this case, *we also have 89 | to coerce `dyn Trait + 'a` to that shorter lifetime.* 90 | 91 | Similar considerations come into play when going between a `&mut Box` 92 | and a `&mut dyn Trait`: 93 | ```rust 94 | #trait Trait {} 95 | #fn foo(d: &mut dyn Trait) {} 96 | #fn bar<'a>(d: &'a mut (dyn Trait + 'a)) { 97 | # foo(d); 98 | # foo(d); 99 | #} 100 | fn baz(bx: &mut Box) { 101 | // If the trait object lifetime could not "shrink" inside the `&mut`, 102 | // we could not make these calls at all 103 | foo(&mut **bx); 104 | bar(&mut **bx); 105 | } 106 | ``` 107 | Here we reborrow `**bx` as `&'a mut (dyn Trait + 'static)` for some 108 | short-lived `'a`, and then coerce that to a `&'a mut (dyn Trait + 'a)`. 109 | 110 | ## Variance in nested context 111 | 112 | The supertype coercion of going from `dyn Trait + 'a` to `dyn Trait + 'b` 113 | when `'a: 'b` *can* happen in deeply nested contexts, provided the trait 114 | object is still in a covariant context. So unlike the `Cell` version 115 | above, this version compiles: 116 | ```rust 117 | # trait Trait {} 118 | fn foo<'l: 's, 's>(v: Vec>>) -> Vec>> { 119 | v 120 | } 121 | ``` 122 | -------------------------------------------------------------------------------- /src/dyn-elision-citations.md: -------------------------------------------------------------------------------- 1 | # Citations 2 | 3 | Finally we compare what we've covered about `dyn Trait` lifetime elision to the 4 | current reference material, and supply some citations to the elision's storied 5 | history. 6 | 7 | ## Summary of differences from the reference 8 | 9 | The official documentation on trait object lifetime elision 10 | [can be found here.](https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes) 11 | 12 | In summary, it states that `dyn Trait` lifetimes have a *default object lifetime bound* which varies based on context. 13 | It states that the default bound only takes effect when the lifetime is *entirely* omitted. When you write out `dyn Trait + '_`, the 14 | [normal lifetime elision rules](https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions) 15 | apply instead. 16 | 17 | In particular, as of this writing, the official documentation states that 18 | > If the trait object is used as a type argument of a generic type then the containing type is first used to try to infer a bound. 19 | > - If there is a unique bound from the containing type then that is the default 20 | > - If there is more than one bound from the containing type then an explicit bound must be specified 21 | > 22 | > If neither of those rules apply, then the bounds on the trait are used: 23 | > - If the trait is defined with a single lifetime bound then that bound is used. 24 | > - If `'static` is used for any lifetime bound then `'static` is used. 25 | > - If the trait has no lifetime bounds, then the lifetime is inferred in expressions and is `'static` outside of expressions. 26 | 27 | Some differences from the reference which we have covered are that 28 | - [inferring bounds in expressions applies to `&T` types unless annotated with a named lifetime](./dyn-elision-advanced.md#an-exception-to-inference-in-function-bodies) 29 | - [inferring bounds in expressions applies to ambiguous types](./dyn-elision-advanced.md#ambiguous-bounds) 30 | - [when trait bounds apply, they override struct bounds, not the other way around](./dyn-elision-trait-bounds.md#when-they-apply-trait-lifetime-bounds-override-struct-bounds) 31 | - [a `'static` trait bound always applies](./dyn-elision-trait-bounds.md#the-static-case) 32 | - [otherwise, whether trait bounds apply or not depends on complicated contextual rules](./dyn-elision-trait-bounds.md#when-and-how-to-trait-lifetime-bounds-apply) 33 | - they always apply in `impl` headers, associated types, and function bodies 34 | - and technically in `static` contexts, with some odd caveats 35 | - whether they apply in function signatures depends on the bounding parameters being late or early bound 36 | - a single parameter can apply to a trait bounds with multiple bounds in this context, introducing new implied lifetime bounds 37 | - [trait bounds override `'_` in function bodies](./dyn-elision-trait-bounds.md#function-bodies) 38 | 39 | And some other under or undocumented behaviors are that 40 | - [aliases override struct definitions](./dyn-elision-advanced.md#iteraction-with-type-aliases) 41 | - [trait bounds create implied bounds on the trait object lifetime](./dyn-elision-trait-bounds.md#trait-lifetime-bounds-create-an-implied-bound) 42 | - [associated type and GAT bounds do not effect the default trait object lifetime](./dyn-elision-advanced.md#associated-types-and-gats) 43 | 44 | ## RFCs, Issues, and PRs 45 | 46 | Trait objects, and trait object lifetime elision in particular, has undergone a lot of evolution over time. 47 | Here we summarize some of the major developments and issues. 48 | 49 | Reminder: a lot of these citations predate the [`dyn Trait` syntax.](https://rust-lang.github.io/rfcs/2113-dyn-trait-syntax.html) 50 | Trait objects used to be just "spelled" as `Trait` in type position, instead of `dyn Trait`. 51 | 52 | - [RFC 0192](https://rust-lang.github.io/rfcs/0192-bounds-on-object-and-generic-types.html#lifetime-bounds-on-object-types) first introduced the trait object lifetime 53 | - [including the "intersection lifetime" consideration](https://rust-lang.github.io/rfcs/0192-bounds-on-object-and-generic-types.html#appendix-b-why-object-types-must-have-exactly-one-bound) 54 | - [RFC 0599](https://rust-lang.github.io/rfcs/0599-default-object-bound.html) first introduced *default* trait object lifetimes (`dyn Trait` lifetime elision) 55 | - [RFC 1156](https://rust-lang.github.io/rfcs/1156-adjust-default-object-bounds.html) superseded RFC 0599 (`dyn Trait` lifetime elision) 56 | - [PR 39305](https://github.com/rust-lang/rust/pull/39305) modified RFC 1156 (unofficially) to [allow more inference in function bodies](dyn-elision-advanced.md#an-exception-to-inference-in-function-bodies) 57 | - [RFC 2093](https://rust-lang.github.io/rfcs/2093-infer-outlives.html#trait-object-lifetime-defaults) defined [how `struct` bounds interact with `dyn Trait` lifetime elision](dyn-elision-advanced.md#guiding-behavior-of-your-own-types) 58 | - [Issue 100270](https://github.com/rust-lang/rust/issues/100270) notes that type aliases take precedent in terms of RFC 2093 `dyn Trait` lifetime elision rules 59 | - [Issue 47078](https://github.com/rust-lang/rust/issues/47078) notes that being late-bound influences `dyn Trait` lifetime elision 60 | 61 | -------------------------------------------------------------------------------- /src/dyn-trait-box-impl.md: -------------------------------------------------------------------------------- 1 | # `impl Trait for Box` 2 | 3 | Let's look at how one implements `Trait for Box`. One 4 | thing to note off that bat is that most methods are going to involve 5 | calling a method of the `dyn Trait` *inside* of our box, but if we just 6 | use `self.method()` we would instantly recurse with the very method 7 | we're writing (`>::method`)! 8 | 9 | ***We need to take care to call `::method` and not `>::method` 10 | in those cases to avoid infinite recursion.*** 11 | 12 | Now that we've highlighted that consideration, let's dive right in: 13 | ```rust 14 | trait Trait { 15 | fn look(&self); 16 | fn boop(&mut self); 17 | fn bye(self) where Self: Sized; 18 | } 19 | 20 | impl Trait for Box { 21 | fn look(&self) { 22 | // We do NOT want to do this! 23 | // self.look() 24 | 25 | // That would recursively call *this* function! 26 | // We need to call `::look`. 27 | 28 | // Any of the below forms work, it depends on 29 | // how explicit you want to be. 30 | 31 | // Very explicit 32 | // ::look(&**self) 33 | 34 | // Yay auto-deref for function parameters 35 | // ::look(self) 36 | 37 | // Very succinct and a "makes sense once you've 38 | // seen it enough times" form. The first deref 39 | // is for the reference (`&Self`) and the second 40 | // deref is for the `Box<_>`. 41 | (**self).look() 42 | } 43 | 44 | fn boop(&mut self) { 45 | // This is similar to the `&self` case 46 | (**self).boop() 47 | } 48 | 49 | fn bye(self) { 50 | // Uh... see below 51 | } 52 | } 53 | ``` 54 | 55 | Oh yeah, that last one. [Remember what we said before?](dyn-trait-impls.md#boxdyn-trait-and-dyn-trait-do-not-automatically-implement-trait) 56 | `dyn Trait` doesn't have this method, but `Box` does. 57 | The compiler isn't going to just guess what to do here (and couldn't if, 58 | say, we needed a return value). We can't move the `dyn Trait` out of 59 | the `Box` because it's unsized. And we can't 60 | [downcast from `dyn Trait`](./dyn-any.md#downcasting-methods-are-not-trait-methods) 61 | either; even if we could, it would rarely help here, as we'd have to both 62 | impose a `'static` constraint and also know every type that implements our 63 | trait to attempt downcasting on each one (or have some other clever scheme 64 | for more efficient downcasting). 65 | 66 | Ugh, no wonder `Box` doesn't implement `Trait` automatically. 67 | 68 | Assuming we want to call `Trait::bye` on the erased type, are we out of luck? 69 | No, there are ways to work around this: 70 | ```rust 71 | // Supertrait bound 72 | trait Trait: BoxedBye { 73 | fn bye(self); 74 | } 75 | 76 | trait BoxedBye { 77 | // Unlike `self: Self`, this does *not* imply `Self: Sized` and 78 | // thus *will* be available for `dyn BoxedBye + '_`... and for 79 | // `dyn Trait + '_` too, automatically. 80 | fn boxed_bye(self: Box); 81 | } 82 | 83 | // We implement it for all `Sized` implementors of `trait: Trait` by 84 | // unboxing and calling `Trait::bye` 85 | impl BoxedBye for T { 86 | fn boxed_bye(self: Box) { 87 | ::bye(*self) 88 | } 89 | } 90 | 91 | impl Trait for Box { 92 | fn bye(self) { 93 | // This time we pass `self` not `*self` 94 | ::boxed_bye(self); 95 | } 96 | } 97 | ``` 98 | 99 | By adding the supertrait bound, the compiler will supply an implementation of 100 | `BoxedBye for dyn Trait + '_`. That implementation will call the implementation 101 | of `BoxedBye` for `Box`, where `Erased` is the erased base type. That 102 | is our blanket implementation, which unboxes `Erased` and calls `Erased`'s 103 | `Trait::bye`. 104 | 105 | The signature of `::boxed_bye` has a receiver with the 106 | type `Box`, which is exactly the same signature as 107 | ` as Trait>::bye`. And that's how we were able to 108 | complete the implementation of `Trait` for `Box`. 109 | 110 | Here's how things flow when calling `Trait::bye` on `Box`: 111 | ```rust,ignore 112 | >::bye (_: Box) -- just passed --> 113 | < dyn Trait >::boxed_bye(_: Box) -- via vtable --> 114 | < Erased >::boxed_bye(_: Box< Erased >) -- via unbox --> 115 | < Erased >::bye (_: Erased ) :) 116 | ``` 117 | 118 | There's rarely a reason to implement `BoxedBye for Box`, since 119 | that takes a nested `Box>` receiver. 120 | 121 | Any `Sized` implementor of `Trait` will get our blanket implementation of 122 | the `BoxedBye` supertrait "for free", so they don't have to do anything 123 | special. 124 | 125 | --- 126 | 127 | The last thing I'll point out is how we did 128 | ```rust 129 | # trait Trait {} 130 | impl Trait for Box { 131 | // this: ^^^^ 132 | # } 133 | ``` 134 | 135 | [We didn't need to require `'static`, so this is more flexible.](dyn-elision-basic.md#impl-headers) 136 | It's also very easy to forget. 137 | 138 | -------------------------------------------------------------------------------- /src/dyn-trait-hash.md: -------------------------------------------------------------------------------- 1 | # Hashable `Box` 2 | 3 | Let's say we have a [`dyn Trait` that implements `Eq`](./dyn-trait-eq.md), but we also want it to implement `Hash` 4 | so that we can use `Box` in a `HashSet` or as the key of a `HashMap` and so on. 5 | 6 |
7 | Here's our starting point 8 | 9 | The only update from [before](./dyn-trait-eq.md) is to require `Eq`: 10 | ```diff 11 | -impl DynCompare for T { 12 | +impl DynCompare for T { 13 | 14 | +impl Eq for dyn DynCompare {} 15 | +impl Eq for dyn Trait {} 16 | ``` 17 | 18 | The complete code: 19 | ```rust 20 | use core::cmp::Ordering; 21 | use std::any::Any; 22 | 23 | trait AsDynCompare: Any { 24 | fn as_any(&self) -> &dyn Any; 25 | fn as_dyn_compare(&self) -> &dyn DynCompare; 26 | } 27 | 28 | // Sized types only 29 | impl AsDynCompare for T { 30 | fn as_any(&self) -> &dyn Any { 31 | self 32 | } 33 | fn as_dyn_compare(&self) -> &dyn DynCompare { 34 | self 35 | } 36 | } 37 | 38 | trait DynCompare: AsDynCompare { 39 | fn dyn_eq(&self, other: &dyn DynCompare) -> bool; 40 | fn dyn_partial_cmp(&self, other: &dyn DynCompare) -> Option; 41 | } 42 | 43 | impl DynCompare for T { 44 | fn dyn_eq(&self, other: &dyn DynCompare) -> bool { 45 | if let Some(other) = other.as_any().downcast_ref::() { 46 | self == other 47 | } else { 48 | false 49 | } 50 | } 51 | 52 | fn dyn_partial_cmp(&self, other: &dyn DynCompare) -> Option { 53 | other 54 | .as_any() 55 | .downcast_ref::() 56 | .and_then(|other| self.partial_cmp(other)) 57 | } 58 | } 59 | 60 | impl Eq for dyn DynCompare {} 61 | impl PartialEq for dyn DynCompare { 62 | fn eq(&self, other: &dyn DynCompare) -> bool { 63 | self.dyn_eq(other) 64 | } 65 | } 66 | 67 | impl PartialOrd for dyn DynCompare { 68 | fn partial_cmp(&self, other: &dyn DynCompare) -> Option { 69 | self.dyn_partial_cmp(other) 70 | } 71 | } 72 | 73 | trait Trait: DynCompare {} 74 | impl Trait for i32 {} 75 | impl Trait for bool {} 76 | 77 | impl Eq for dyn Trait {} 78 | impl PartialEq for dyn Trait { 79 | fn eq(&self, other: &dyn Trait) -> bool { 80 | self.as_dyn_compare() == other.as_dyn_compare() 81 | } 82 | } 83 | 84 | impl PartialOrd for dyn Trait { 85 | fn partial_cmp(&self, other: &dyn Trait) -> Option { 86 | self.as_dyn_compare().partial_cmp(other.as_dyn_compare()) 87 | } 88 | } 89 | 90 | impl PartialEq<&Self> for Box { 91 | fn eq(&self, other: &&Self) -> bool { 92 | ::eq(self, *other) 93 | } 94 | } 95 | 96 | impl PartialOrd<&Self> for Box { 97 | fn partial_cmp(&self, other: &&Self) -> Option { 98 | ::partial_cmp(self, *other) 99 | } 100 | } 101 | ``` 102 | 103 | --- 104 | 105 |
106 | 107 | Similarly to [when we looked at `Clone`,](./dyn-trait-clone.md) `Hash` is not an object safe trait. So we can't 108 | just add `Hash` as a supertrait bound. This time it's not because it requires `Sized`, though, it's because 109 | [it has a generic method:](https://doc.rust-lang.org/std/hash/trait.Hash.html#tymethod.hash) 110 | ```rust 111 | # use core::hash::Hasher; 112 | pub trait Hash { 113 | fn hash(&self, state: &mut H) 114 | where H: Hasher; 115 | } 116 | ``` 117 | 118 | Fortunately, we can use an [erased trait](./dyn-trait-erased.md) approach: 119 | 120 | ```rust 121 | use std::collections::HashSet; 122 | use core::hash::{Hash, Hasher}; 123 | 124 | trait DynHash { 125 | fn dyn_hash(&self, state: &mut dyn Hasher); 126 | } 127 | 128 | // impl DynHash for T { 129 | impl DynHash for T { 130 | fn dyn_hash(&self, mut state: &mut dyn Hasher) { 131 | self.hash(&mut state) 132 | } 133 | } 134 | 135 | impl Hash for dyn DynHash + '_ { 136 | fn hash(&self, state: &mut H) { 137 | self.dyn_hash(state) 138 | } 139 | } 140 | ``` 141 | Now is a good time to point out a couple of things we're relying on: 142 | - [`Hasher`](https://doc.rust-lang.org/std/hash/trait.Hasher.html) *is* object safe 143 |

If this wasn't the case, we couldn't take a `&mut dyn Hasher` in our `dyn_hash` method. 144 | 145 | - The generic `H` in `Hash::hash` has an implicit `Sized` bound 146 |

If this wasn't the case, we couldn't coerce the `&mut H` to a `&mut dyn Hasher` 147 | in our implementation of `Hash` for `dyn DynHash`. 148 | 149 | This demonstrates that *relaxing* a `Sized` bound can be a breaking change! 150 | 151 | 152 | Moving on, we still need to wire up this new functionality to our own trait. 153 | ```diff 154 | -trait Trait: DynCompare; 155 | +trait Trait: DynCompare + DynHash {} 156 | ``` 157 | ```rust 158 | # use core::hash::{Hash, Hasher}; 159 | # trait DynHash { fn dyn_hash(&self, _: &mut dyn Hasher); } 160 | # trait Trait: DynHash {} 161 | // Same as what we did for `dyn DynHash` 162 | impl Hash for dyn Trait { 163 | fn hash(&self, state: &mut H) { 164 | self.dyn_hash(state) 165 | } 166 | } 167 | ``` 168 | [And that's it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=de3908f78bbeeb4da109ca0fa8e1d53b) 169 | ```rust,ignore 170 | let bx1a: Box = Box::new(1); 171 | let bx1b: Box = Box::new(1); 172 | let bx2: Box = Box::new(2); 173 | let bx3: Box = Box::new(true); 174 | 175 | let hm: HashSet<_> = HashSet::from_iter([bx1a, bx1b, bx2, bx3].into_iter()); 176 | assert_eq!(hm.len(), 3); 177 | 178 | ``` 179 | 180 | ## Closing remarks 181 | 182 | [Borrowing a concrete type](./dyn-trait-borrow.md) is probably a better approach if it applies 183 | to your use case, since it doesn't require `Any + 'static`. 184 | 185 | Although terribly inefficient, an implementation of `Hash` that returns the hash for everything 186 | is a correct implementation. So are other "rough approximations", like if we only hashed the 187 | `TypeId`. All that's required for logical behavior is that two equal values must also have 188 | equal hashes. So arguably we didn't need to go to such lengths to get the exact hashes of the 189 | values. 190 | 191 | You may have noticed this commented out line: 192 | ```rust,ignore 193 | // impl DynHash for T { 194 | impl DynHash for T { 195 | ``` 196 | 197 | The reason I went with the less general implementation is two-fold: 198 | - It wasn't needed for the example 199 | - Prudency: because we `impl Hash for dyn DynHash`, it technically overlaps with the compiler's implementation of `DynHash for dyn DynHash`. 200 | [See the final paragraph of this subsection.](./dyn-trait-impls.md#the-implementation-cannot-be-directly-overrode) 201 | 202 | 203 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/pf-shared-nested.md: -------------------------------------------------------------------------------- 1 | # `&'a Struct<'a>` and covariance 2 | 3 | Here's a situation that looks similar to [borrowing something forever,](./pf-borrow-forever.md) 4 | but is actually somewhat different. 5 | 6 | ```rust 7 | struct Person<'a> { 8 | given: &'a str, 9 | sur: &'a str, 10 | } 11 | 12 | impl<'a> Person<'a> { 13 | // vvvvvvvv `&'a Person<'a>` 14 | fn given(&'a self) -> &'a str { 15 | self.given 16 | } 17 | } 18 | 19 | fn example(person: Person<'_>) { 20 | // Unlike when we borrowed something forever, this compiles 21 | let _one = person.given(); 22 | let _two = person.given(); 23 | } 24 | ``` 25 | 26 | The difference is that `&U` is covariant in `U`, so 27 | [lifetimes can "shrink" behind the reference](./st-invariance.md) 28 | (unlike `&mut U`, which is invariant in `U`). `Person<'a>` is also 29 | covariant in `'a`, because all of our uses of `'a` in the definition 30 | are in covariant position. 31 | 32 | What all this means is that `&'long Person<'long>` can coerce to 33 | a `&'short Person<'short>`. As a result, calling `Person::given` 34 | doesn't have to borrow the `person` forever -- it only has to borrow 35 | `person` for as long as the return value is used. 36 | 37 | Note that the covariance is required! A shared nested borrow where 38 | the inner lifetime is invariant is still almost as bad as the 39 | "borrowed forever" `&mut` case. Most of this page talks about the 40 | covariant case; we'll consider the invariant case [at the end.](#the-invariant-case) 41 | 42 | ## This is still a yellow flag 43 | 44 | Even though it's not as problematic as the `&mut` case, there is 45 | still something non-ideal about that signature: it forces the 46 | borrow of `person` to be longer than it needs to be. For example, 47 | this fails: 48 | ```rust 49 | # struct Person<'a> { given: &'a str } 50 | # impl<'a> Person<'a> { fn given(&'a self) -> &'a str { self.given } } 51 | # struct Stork(String); 52 | # impl Stork { fn deliver(&self, _: usize) -> Person<'_> { Person { given: &self.0 } } } 53 | fn example(stork: Stork) { 54 | let mut last = ""; 55 | for i in 0..10 { 56 | let person = stork.deliver(i); 57 | last = person.given(); 58 | // ... 59 | } 60 | println!("Last: {last}"); 61 | } 62 | ``` 63 | 64 | `person` has to remain borrowed for as long the return value is around, 65 | because we said `&self` and the returned `&str` have to have the same 66 | lifetime. 67 | 68 | If we instead allow the lifetimes to be different: 69 | ```rust 70 | # struct Person<'a> { given: &'a str } 71 | # struct Stork(String); 72 | # impl Stork { fn deliver(&self, _: usize) -> Person<'_> { Person { given: &self.0 } } } 73 | impl<'a> Person<'a> { 74 | // vvvvv We removed `'a` from `&self` 75 | fn given(&self) -> &'a str { 76 | self.given 77 | } 78 | } 79 | 80 | fn example(stork: Stork) { 81 | let mut last = ""; 82 | for i in 0..10 { 83 | let person = stork.deliver(i); 84 | last = person.given(); 85 | // ... 86 | } 87 | println!("Last: {last}"); 88 | } 89 | ``` 90 | Then the borrow of `person` can end immediately after the call, even 91 | while the return value remains usable. This is possible because we're 92 | just copying the reference out. Or if you prefer to think of it another 93 | way, we're handing out a reborrow of an existing borrow we were holding 94 | on to, and not borrowing something we owned ourselves. 95 | 96 | So now the `stork` still has to be around for as long as `last` is used, 97 | but the `person` can go away at the end of the loop. 98 | 99 | Allowing the lifetimes to be different is normally what you want to do 100 | when you have a struct that's just managing a borrowed resource in some 101 | way -- when you hand out pieces of the borrowed resource, you want them 102 | to be tied to the lifetime of the original borrow and not the lifetime of 103 | `&self` or `&mut self` on the method call. [It's how borrowing iterators 104 | work,](https://doc.rust-lang.org/std/slice/struct.Iter.html#impl-Iterator-for-Iter%3C'a,+T%3E) 105 | for example. 106 | 107 | ## A variation on the theme 108 | 109 | Consider this version of the method: 110 | ```rust 111 | # struct Person<'a> { given: &'a str } 112 | impl<'a> Person<'a> { 113 | fn given(&self) -> &str { 114 | self.given 115 | } 116 | } 117 | ``` 118 | 119 | It has the same downside as `given(&'a self) -> &'a str`: The return 120 | value is tied to `self` and not `'a`. It's easy to make this mistake 121 | when developing borrowing structs, because the lifetime elision rules 122 | nudge you in this direction. It's also harder to spot because there's 123 | no `&'a self` to clue you in. 124 | 125 | ## But sometimes it's perfectly okay 126 | 127 | On the flip side, because of the covariance we discussed at the top of 128 | this page, there's no practical difference between these two methods: 129 | ```rust 130 | # struct Person<'a> { given: &'a str } 131 | impl<'a> Person<'a> { 132 | fn foo(&self) {} 133 | fn bar(&'a self) {} 134 | ``` 135 | 136 | There's no return value to force the lifetime to be longer, so these 137 | methods are going to act the same. There's no reason for the `'a` 138 | on `&'a self`, but it's not hurting anything either. 139 | 140 | Similarly, within a struct there's rarely a benefit to keeping the 141 | nested lifetimes separated, so you might as well use this: 142 | ```rust 143 | struct Cradle<'a> { 144 | person: &'a Person<'a> 145 | } 146 | ``` 147 | Instead of something with two lifetimes. 148 | 149 | (That said, an even better approach is to not have complicated nested-borrow-holding data structures at all.) 150 | 151 | ## The invariant case 152 | 153 | Finally, let's look at a case where it's generally not okay: 154 | A shared nested borrow where the inner borrow is invariant. 155 | 156 | Perhaps the most likely reason this comes up is due to *shared mutability:* the ability 157 | to mutate things that are behind a shared reference (`&`). Some examples from the 158 | standard library include [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html), 159 | [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html), and 160 | [`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html). These 161 | shared mutability types have to be invariant over their generic parameter `T`, 162 | just like how `&mut T` is invariant over `T`. 163 | 164 | Let's see an example, [similar to one we've seen before](./pf-meta.md): 165 | ```rust 166 | # use std::cell::Cell; 167 | #[derive(Debug)] 168 | struct ShareableSnek<'a> { 169 | owned: String, 170 | borrowed: Cell<&'a str>, 171 | } 172 | 173 | impl<'a> ShareableSnek<'a> { 174 | fn bite(&'a self) { 175 | self.borrowed.set(&self.owned); 176 | } 177 | } 178 | 179 | let snek = ShareableSnek { 180 | owned: "🐍".to_string(), 181 | borrowed: Cell::new(""), 182 | }; 183 | 184 | snek.bite(); 185 | 186 | // Unlike the `&mut` case, we can still use `snek`! It's borrowed forever, 187 | // but it's "only" *shared*-borrowed forever. 188 | println!("{snek:?}"); 189 | ``` 190 | 191 | That doesn't seem so bad though, right? Well, it's quite as bad as the `&mut` 192 | case, but it's still usually too restrictive to be useful. 193 | ```rust,compile_fail 194 | # use std::cell::Cell; 195 | # #[derive(Debug)] 196 | # struct ShareableSnek<'a> { 197 | # owned: String, 198 | # borrowed: Cell<&'a str>, 199 | # } 200 | # 201 | # impl<'a> ShareableSnek<'a> { 202 | # fn bite(&'a self) { 203 | # self.borrowed.set(&self.owned); 204 | # } 205 | # } 206 | # 207 | # let snek = ShareableSnek { 208 | # owned: "🐍".to_string(), 209 | # borrowed: Cell::new(""), 210 | # }; 211 | # 212 | snek.bite(); 213 | let _mutable_stuff = &mut snek; 214 | let _move = snek; 215 | 216 | // Having a non-trivial destructor would also cause a failure 217 | ``` 218 | Once it's borrowed forever, the `snek` can only be used in a "shared" way. 219 | It can only be mutated using shared mutability, and it can't be moved -- 220 | it's pinned in place forever. 221 | 222 | -------------------------------------------------------------------------------- /src/dyn-elision-basic.md: -------------------------------------------------------------------------------- 1 | # Basic guidelines and subtleties 2 | 3 | As a reminder, `dyn Trait` is a type constructor which is parameterized with a 4 | lifetime; a fully resolved type includes the lifetime, such as `dyn Trait + 'static`. 5 | The lifetime can be elided in many situations, in which case the actual lifetime 6 | used may take on some default lifetime, or may be inferred. 7 | 8 | When talking about default trait object (`dyn Trait`) lifetimes, we're talking about 9 | situations where the lifetime has been completely elided. If the wildcard lifetime 10 | is used (`dyn Trait + '_`), then [the normal lifetime elision rules](https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions) 11 | usually apply instead. (The exceptions are rare, and you can usually be explicit 12 | instead if you need to.) 13 | 14 | For a completely elided `dyn Trait` lifetime, you can start with these 15 | general guidelines for traits with no lifetime bounds (which are the vast majority): 16 | - In function bodies, the trait object lifetime is inferred (i.e. ignore the following bullets) 17 | - For references like `&'a dyn Trait`, the default is the same as the reference lifetime (`'a`) 18 | - For `dyn`-supporting `std` types with lifetime parameters such as 19 | [`Ref<'a, T>`](https://doc.rust-lang.org/std/cell/struct.Ref.html), it is also `'a` 20 | - For non-lifetime-parameter types like `Box`, and for bare `dyn Trait`, it's `'static` 21 | 22 | And for the (rare) trait with lifetime bounds: 23 | - If the trait has a `'static` bound, the trait object lifetime is always `'static` 24 | - If the trait has only non-`'static` lifetime bounds, [you're better off being explicit](./dyn-elision-trait-bounds.md) 25 | 26 | 27 | This is a close enough approximation to let you understand `dyn Trait` 28 | lifetime elision most of the time, but there are exceptions to these 29 | guidelines (which are explored on the next couple of pages). 30 | 31 | There are also a few subtleties worth pointing out *within* these guidelines, 32 | which are covered immediately below. 33 | 34 | ## Default `'static` bound gotchas 35 | 36 | The most likely scenario to run into an error about `dyn Trait` lifetime is 37 | when `Box` or similar is involved, resulting an implicit `'static` constraint. 38 | 39 | Those errors can often be addressed by either adding an explicit `'static` 40 | bound, or by overriding the implicit `'static` lifetime. In particular, using 41 | `'_` will usually result in the "normal" (non-`dyn Trait`) lifetime elision for 42 | the given context. 43 | 44 | ```rust 45 | trait Trait {} 46 | impl Trait for &T {} 47 | 48 | // Remove `+ 'static` to see an error 49 | fn with_explicit_bound<'a, T: Trait + 'static> (t: T) -> Box { 50 | Box::new(t) 51 | } 52 | 53 | // Remove `+ 'a` (in either position) to see an error 54 | fn with_nonstatic_box<'a, T: Trait + 'a>(t: T) -> Box { 55 | Box::new(t) 56 | } 57 | 58 | // Remove `+ '_` to see an error 59 | fn with_fn_lifetime_elision(t: &impl Trait) -> Box { 60 | Box::new(t) 61 | } 62 | ``` 63 | 64 | This can be particularly confusing within a function body, where a 65 | `Box` variable annotation acts differently from a `Box` 66 | function input parameter annotation: 67 | ```rust 68 | # trait Trait {} 69 | # impl Trait for &i32 {} 70 | // In this context, the elided lifetime is `'static` 71 | fn requires_static(_: Box) {} 72 | 73 | fn example() { 74 | let local = 0; 75 | 76 | // In this context, the annotation means `Box`! 77 | // That is why it can compile on it's own, with the local reference. 78 | let bx: Box = Box::new(&local); 79 | 80 | // So despite using the same syntax, this call cannot compile. 81 | // Uncomment it to see the compilation error. 82 | // requires_static(bx); 83 | } 84 | ``` 85 | 86 | ## `impl` headers 87 | 88 | The `dyn Trait` lifetime elision applies in `impl` headers, which can lead to 89 | implementations being less general than possible or desired: 90 | ```rust 91 | trait Trait {} 92 | trait Two {} 93 | 94 | impl Two for Box {} 95 | impl Two for &dyn Trait {} 96 | ``` 97 | 98 | `Two` is implemented for 99 | - `Box` 100 | - `&'a (dyn Trait + 'a)` for any `'a` (the lifetimes must match) 101 | 102 | Consider using implementations like the following if possible, as they are 103 | more general: 104 | ```rust 105 | # trait Trait {} 106 | # trait Two {} 107 | // Implemented for all lifetimes 108 | impl Two for Box {} 109 | 110 | // Implemented for all lifetimes such that the inner lifetime is 111 | // at least as long as the outer lifetime 112 | impl Two for &(dyn Trait + '_) {} 113 | ``` 114 | 115 | ## Alias gotchas 116 | 117 | Similar to `impl` headers, elision will apply when defining a type alias: 118 | ```rust,compile_fail 119 | trait MyTraitICouldNotThinkOfAShortNameFor {} 120 | 121 | // This is an alias to `dyn ... + 'static`! 122 | type MyDyn = dyn MyTraitICouldNotThinkOfAShortNameFor; 123 | 124 | // The default does not "override" the type alias and thus 125 | // requires the trait object lifetime to be `'static` 126 | fn foo(_: &MyDyn) {} 127 | 128 | // As per the `dyn` elision rules, this requires the trait 129 | // object lifetime to be the same as the reference... 130 | fn bar(d: &dyn MyTraitICouldNotThinkOfAShortNameFor) { 131 | // ...and thus this fails as the lifetime cannot be extended 132 | foo(d); 133 | } 134 | ``` 135 | 136 | More generally, elision does not "penetrate" or alter type aliases. 137 | This includes the `Self` alias within implementation blocks. 138 | ```rust,compile_fail 139 | trait Trait {} 140 | 141 | impl dyn Trait { 142 | // Error: requires `T: 'static` 143 | fn f(t: &T) -> &Self { t } 144 | } 145 | 146 | impl<'a> dyn Trait + 'a { 147 | // Error: requires `T: 'a` 148 | fn g(t: &T) -> &Self { t } 149 | } 150 | ``` 151 | 152 | See also [how type aliases with parameters behave.](./dyn-elision-advanced.md#iteraction-with-type-aliases) 153 | 154 | 155 | ## `'static` traits 156 | 157 | When the trait itself is `'static`, the trait object lifetime has an implied 158 | `'static` bound. Therefore if you name the trait object lifetime explicitly, 159 | the name you give it will also have an implied `'static` bound. So here: 160 | ```rust,compile_fail 161 | # use core::any::Any; 162 | // n.b. trait `Any` has a `'static` bound 163 | fn example<'a>(_: &'a (dyn Any + 'a)) {} 164 | 165 | fn main() { 166 | let local = (); 167 | example(&local); 168 | } 169 | ``` 170 | 171 | We get an error that the borrow of `local` must be `'static`. The problem is 172 | that `'a` in `example` has inherited the `'static` bound (`'a: 'static`), and 173 | we also gave the outer reference the lifetime of `'a`. This is a case where 174 | we don't actually want them to be the same. 175 | 176 | The most ergonomic solution is to always completely elide the trait object 177 | lifetime when the trait itself has a `'static` bound. Unlike other cases, 178 | the trait object lifetime is independent of the outer reference lifetime when 179 | the trait itself has a `'static` bound, so this compiles: 180 | ```rust 181 | # use core::any::Any; 182 | // This is `&'a (dyn Any + 'static)` and `'a` doesn't have to be `'static` 183 | fn example(_: &dyn Any) {} 184 | 185 | fn main() { 186 | let local = (); 187 | example(&local); 188 | } 189 | ``` 190 | 191 | `Any` is the most common trait with a `'static` bound, i.e. the most likely 192 | reason for you to encounter this scenario. 193 | 194 | ## `static` contexts 195 | 196 | In some contexts like when declaring a `static`, it's possible to elide the 197 | lifetime of types like references; doing so will result in `'static` being 198 | used for the elided lifetime: 199 | ```rust 200 | // The elided lifetime is `'static` 201 | static S: &str = ""; 202 | const C: &str = ""; 203 | ``` 204 | 205 | As a result, elided `dyn Trait` lifetimes will by default also be `'static`, 206 | matching the inferred lifetime of the reference. In contrast, this fails 207 | due to the outer lifetime being `'static`: 208 | ```rust,compile_fail 209 | # trait Trait {} 210 | # impl Trait for () {} 211 | struct S<'a: 'b, 'b>(&'b &'a str); 212 | impl<'a: 'b, 'b> S<'a, 'b> { 213 | const T: &(dyn Trait + 'a) = &(); 214 | } 215 | ``` 216 | 217 | In this context, eliding all the lifetimes is again *usually* what you want. 218 | -------------------------------------------------------------------------------- /src/dyn-trait-erased.md: -------------------------------------------------------------------------------- 1 | # Erased traits 2 | 3 | Let's say you have an existing trait which works well for the most part: 4 | ```rust 5 | pub mod useful { 6 | pub trait Iterable { 7 | type Item; 8 | type Iter<'a>: Iterator where Self: 'a; 9 | fn iter(&self) -> Self::Iter<'_>; 10 | fn visit(&self, mut f: F) { 11 | for item in self.iter() { 12 | f(item); 13 | } 14 | } 15 | } 16 | 17 | impl Iterable for &I { 18 | type Item = ::Item; 19 | type Iter<'a> = ::Iter<'a> where Self: 'a; 20 | fn iter(&self) -> Self::Iter<'_> { 21 | ::iter(*self) 22 | } 23 | } 24 | 25 | impl Iterable for Box { 26 | type Item = ::Item; 27 | type Iter<'a> = ::Iter<'a> where Self: 'a; 28 | fn iter(&self) -> Self::Iter<'_> { 29 | ::iter(&**self) 30 | } 31 | } 32 | 33 | impl Iterable for Vec { 34 | type Item = T; 35 | type Iter<'a> = std::slice::Iter<'a, T> where Self: 'a; 36 | fn iter(&self) -> Self::Iter<'_> { 37 | <[T]>::iter(self) 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | However, it's not [`dyn` safe](./dyn-safety.md) and you wish it was. 44 | Even if we get support for GATs in `dyn Trait` some day, there 45 | are no plans to support functions with generic type parameters 46 | like `Iterable::visit`. Besides, you want the functionality now, 47 | not "some day". 48 | 49 | Perhaps you also have a lot of code utilizing this useful trait, 50 | and you don't want to redo everything. Maybe it's not even your 51 | own trait. 52 | 53 | This may be a case where you want to provide an "erased" version 54 | of the trait to make it `dyn` safe. The general idea is to use 55 | `dyn` (type erasure) to replace all the non-`dyn`-safe uses such 56 | as GATs and type-parameterized methods. 57 | 58 | ```rust 59 | pub mod erased { 60 | // This trait is `dyn` safe 61 | pub trait Iterable { 62 | type Item; 63 | // No more GAT 64 | fn iter(&self) -> Box + '_>; 65 | // No more type parameter 66 | fn visit(&self, f: &mut dyn FnMut(&Self::Item)); 67 | } 68 | } 69 | ``` 70 | 71 | We want to be able to create a `dyn erased::Iterable` from anything 72 | that is `useful::Iterable`, so we need a blanket implementation to 73 | connect the two: 74 | ```rust 75 | #fn main() {} 76 | #pub mod useful { 77 | # pub trait Iterable { 78 | # type Item; 79 | # type Iter<'a>: Iterator where Self: 'a; 80 | # fn iter(&self) -> Self::Iter<'_>; 81 | # fn visit(&self, f: F); 82 | # } 83 | #} 84 | pub mod erased { 85 | use crate::useful; 86 | # pub trait Iterable { 87 | # type Item; 88 | # fn iter(&self) -> Box + '_>; 89 | # fn visit(&self, f: &mut dyn FnMut(&Self::Item)) { 90 | # for item in self.iter() { 91 | # f(item); 92 | # } 93 | # } 94 | # } 95 | 96 | impl Iterable for I { 97 | type Item = ::Item; 98 | fn iter(&self) -> Box + '_> { 99 | Box::new(useful::Iterable::iter(self)) 100 | } 101 | // By not using a default function body, we can avoid 102 | // boxing up the iterator 103 | fn visit(&self, f: &mut dyn FnMut(&Self::Item)) { 104 | for item in ::iter(self) { 105 | f(item) 106 | } 107 | } 108 | } 109 | } 110 | ``` 111 | 112 | We're also going to want to pass our `erased::Iterable`s to functions 113 | that have a `useful::Iterable` trait bound. However, we can't add 114 | that as a supertrait, because that would remove the `dyn` safety. 115 | 116 | The purpose of our `erased::Iterable` is to be able to type-erase to 117 | `dyn erased::Iterable` anyway though, so instead we just implement 118 | `useful::Iterable` directly on `dyn erased::Iterable`: 119 | ```rust 120 | #fn main() {} 121 | #pub mod useful { 122 | # pub trait Iterable { 123 | # type Item; 124 | # type Iter<'a>: Iterator where Self: 'a; 125 | # fn iter(&self) -> Self::Iter<'_>; 126 | # fn visit(&self, f: F); 127 | # } 128 | #} 129 | pub mod erased { 130 | use crate::useful; 131 | # pub trait Iterable { 132 | # type Item; 133 | # fn iter(&self) -> Box + '_>; 134 | # fn visit(&self, f: &mut dyn FnMut(&Self::Item)) { 135 | # for item in self.iter() { 136 | # f(item); 137 | # } 138 | # } 139 | # } 140 | # impl Iterable for I { 141 | # type Item = ::Item; 142 | # fn iter(&self) -> Box + '_> { 143 | # Box::new(useful::Iterable::iter(self)) 144 | # } 145 | # fn visit(&self, f: &mut dyn FnMut(&Self::Item)) { 146 | # for item in ::iter(self) { 147 | # f(item) 148 | # } 149 | # } 150 | # } 151 | 152 | impl useful::Iterable for dyn Iterable + '_ { 153 | type Item = Item; 154 | type Iter<'a> = Box + 'a> where Self: 'a; 155 | fn iter(&self) -> Self::Iter<'_> { 156 | Iterable::iter(self) 157 | } 158 | // Here we can choose to override the default function body to avoid 159 | // boxing up the iterator, or we can use the default function body 160 | // to avoid dynamic dispatch of `F`. I've opted for the former. 161 | fn visit(&self, mut f: F) { 162 | ::visit(self, &mut f) 163 | } 164 | } 165 | } 166 | ``` 167 | 168 | Technically our blanket implementation of `erased::Iterable` now applies to 169 | `dyn erased::Iterable`, but things still work out due to some 170 | [language magic.](./dyn-trait-impls.md#the-implementation-cannot-be-directly-overrode) 171 | 172 | The blanket implementations of `useful::Iterable` in the `useful` module gives 173 | us implementations for `&dyn erased::Iterable` and `Box`, 174 | so now we're good to go! 175 | 176 | ## Mindful implementations and their limitations 177 | 178 | You may have noticed how we took care to avoid boxing the iterator when possible 179 | by being mindful of how we implemented some of the methods, for example not 180 | having a default body for `erased::Iterable::visit`, and then overriding the 181 | default body of `useful::Iterable::visit`. This can lead to better performance 182 | but isn't necessarily critical, so long as you avoid things like accidental 183 | infinite recursion. 184 | 185 | How well did we do on this front? 186 | [Let's take a look in the playground.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=31dbe5ae8a7a6b7677ed942962424e03) 187 | 188 | Hmm, perhaps not as well as we hoped! `::visit` 189 | avoids the boxing as designed, but `Box`'s `visit` still boxes the 190 | iterator. 191 | 192 | Why is that? It is because the implementation for the `Box` is supplied by the `useful` 193 | module, and that implementation uses the default body. In order to avoid the boxing, 194 | [it would need to recurse to the underlying implementation instead.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=02ae6370cdd7f7b5b1586e0281e090d2) 195 | That way, the call to `visit` will "drill down" until the implementation for 196 | `dyn erased::Iterable::visit`, which takes care to avoid the boxed iterator. Or 197 | phrased another way, the recursive implementations "respects" any overrides of the 198 | default function body by other implementors of `useful::Iterable`. 199 | 200 | Since the original trait might not even be in your crate, this might be out of your 201 | control. Oh well, so it goes; maybe submit a PR 🙂. In this particular case you 202 | could take care to pass `&dyn erased::Iterable` by coding `&*boxed_erased_iterable`. 203 | 204 | Or maybe it doesn't really matter enough to bother in practice for your use case. 205 | 206 | ## Real world examples 207 | 208 | Perhaps the most popular crate to use this pattern is 209 | [the `erased-serde` crate.](https://crates.io/crates/erased-serde) 210 | 211 | Another use case is [working with `async`-esque traits,](https://smallcultfollowing.com/babysteps/blog/2021/10/15/dyn-async-traits-part-6/) 212 | which tends to involve a lot of type erasure and unnameable types. 213 | -------------------------------------------------------------------------------- /src/misc-slice.md: -------------------------------------------------------------------------------- 1 | # Slice layout 2 | 3 | It's not uncommon for people on [the forum](https://users.rust-lang.org/) 4 | to ask why it's conventional to have `&[T]` as an argument insteaed of 5 | `&Vec`, or to ask about the layout of slices more generally. Or to 6 | ask analogous questions about `&str` and `String`, et cetera. 7 | 8 | This page exists to be a useful citation for such questions. 9 | 10 | If you want, you can skip ahead to the [graphical layout.](#graphical-layout) 11 | 12 | ## What is a slice anyway? 13 | 14 | The terminology around slices tends to be pretty loose. I'll try to keep 15 | it more formal on this page, but when you read something about a "slice" 16 | elsewhere, keep in mind that it may be referring to any of `[T]`, `&[T]`, 17 | `&mut [T]`, or even other types of references to `[T]` (`Box<[T]>`, `Arc<[T]>`, ...). 18 | 19 | This is the case not just for casual material, but for 20 | [official documentation](https://doc.rust-lang.org/std/primitive.slice.html) 21 | and other technical material. You just have to figure out which one or 22 | ones they are specifically talking about from context. 23 | 24 | With that out of the way, let me intoduce some terminology for this page: 25 | 26 | - A slice, `[T]`, is a series of `T` in contiguous memory (layed out one after 27 | another, with proper alignment). The length is only known at run time; we say 28 | it is a dynamically sized type (DST), or an unsized type, or a type that does 29 | not implement [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html). 30 | 31 | - A shared slice, `&[T]`, is a shared reference to a slice. It's a wide 32 | reference consisting of a pointer to the memory of the slice, and the number 33 | of elements in the slice. 34 | 35 | - An exclusive slice, `&mut [T]`, is like a shared slice, but the borrow is 36 | exclusive (so you can e.g. overwrite elements through it). 37 | 38 | - There are other wide pointer variations like boxed slices (`Box<[T]>`) and 39 | so on; we'll mention a few more momentarily. 40 | 41 | Note that while slices are unsized, the wide pointers to slices (like `&[T]`) 42 | are sized. 43 | 44 | ### Where is a slice? 45 | 46 | Slices can be on the heap, but also on the stack, or in static memory, or 47 | anywhere else. The type doesn't "care" where it is. Therefore, you can't 48 | be sure where a pointer to a slice points unless the pointer itself has 49 | further guarantees. 50 | 51 | For example, if you have a `Box<[T]>`, then any `T` within are on the 52 | heap, because that's a guarantee of 53 | [`Box<_>`.](https://doc.rust-lang.org/std/boxed/struct.Box.html) 54 | (N.b. if `T` is zero sized, they are not actually stored anywhere.) 55 | So in that *particular* case, we could say the slice `[T]` is on the heap. 56 | 57 | ## What is a `Vec<_>` anyway? 58 | 59 | A [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) is growable buffer 60 | that owns and stores `T`s in contiguous memory, on the heap. You can conceptually 61 | think of this as something that owns a slice `[T]` (or more accurately, 62 | `[MaybeUninit]`). You can index into a `Vec` with a range and get back a 63 | shared or exclusive slice. 64 | 65 | [A `Vec<_>` consists of a pointer, capacity, and length.](https://doc.rust-lang.org/std/vec/struct.Vec.html#guarantees) 66 | 67 | ## Other types 68 | 69 | A [`String`](https://doc.rust-lang.org/std/string/struct.String.html) is, under the hood, 70 | like a `Vec` which has additional guarantees -- namely, that the bytes are valid UTF8. 71 | A [`&str`](https://doc.rust-lang.org/std/primitive.str.html) is like a `&[u8]` that has the 72 | same guarantee. You can index into a `String` with a range and get back a `&str` 73 | (or `&mut str`). Like `[u8]`, a `str` is unsized, which is why you're almost always working 74 | with a `&str` or other pointer instead. 75 | 76 | So the relationship betweeen `str` and `String` is the same as between `[T]` and `Vec`. 77 | There are other pairs of types with the same relationship: 78 | - [`Path`](https://doc.rust-lang.org/std/path/struct.Path.html) and [`PathBuf`](https://doc.rust-lang.org/std/path/struct.PathBuf.html) 79 | - [`OsStr`](https://doc.rust-lang.org/std/ffi/struct.OsStr.html) and [`OsString`](https://doc.rust-lang.org/std/ffi/struct.OsString.html) 80 | - [`CStr`](https://doc.rust-lang.org/std/ffi/struct.CStr.html) and [`CString`](https://doc.rust-lang.org/std/ffi/struct.CString.html) 81 | 82 | These `std` types generally have a [`ToOwned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#implementors) 83 | relationship and a [`Borrow`](https://doc.rust-lang.org/std/borrow/trait.Borrow.html#implementors) relationship. 84 | 85 | Even more data structures that can be considered a form of *owned* slices include: 86 | - [`[T; N]`](https://doc.rust-lang.org/std/primitive.array.html) is an array with a 87 | compile-time known length (i.e. it's a fixed-size array). It is like a slice (`[T]`), 88 | but it is `Sized`, as the the length is known at compile time. The length is also part 89 | of the type. It's not growable. 90 | 91 | - `Box<[T]>`, a "boxed slice"; this is similar to a `Vec` in that it owns the `T` 92 | and stores them contiguously on the heap. Unlike a `Vec`, the buffer is not growable 93 | (or shrinkable) through a `&mut Box<[T]>`; you would have to allocate new storage and 94 | move the elements over. The length of a boxed slice is stored at runtime, and isn't 95 | known at compile time. Therefore, like a shared slice, it consists of a pointer and 96 | a length. 97 | 98 | - `Arc<[T]>` and `Rc<[T]>` are shared owneship variations on `Box<[T]>`. 99 | 100 | - There are similar variations for string-like types (`Box`, `Arc`, `Rc`, ...) 101 | 102 | - And other combinations too (`Box<[T; N]>`, etc.) 103 | 104 | You can create shared slices to these other types of owned slices as well. 105 | 106 | Technically, just a single `T` is like a `[T; 1]` (it has the same layout in memory). 107 | So if you squint just right, every owned `T` is also a form of owned slice, but with 108 | a compile-time known length of 1. And indeed, you can create 109 | [`&[T]`](https://doc.rust-lang.org/std/slice/fn.from_ref.html) 110 | and [`&mut [T]`](https://doc.rust-lang.org/std/slice/fn.from_mut.html) 111 | ([and array versions too](https://doc.rust-lang.org/std/array/index.html#functions)) 112 | from `&T` and `&mut T`. 113 | 114 | ## Graphical layout 115 | 116 | Here's a graphical representation of the layout of slices, shared slices, 117 | `Vec`, and `&Vec`. 118 | 119 | ``` 120 | +---+---+---+---+---+---+---+---+ 121 | | Pointer | Length | &[T] (or &str, &Path, Box<[T]>, ...) 122 | +---+---+---+---+---+---+---+---+ 123 | | 124 | V 125 | +---+---+---+---+---+---+---+---+ 126 | | D | A | T | A | . | . | . | ...... [T] (or str, Path, ...) 127 | +---+---+---+---+---+---+---+---+ 128 | ^ 129 | | 130 | +---+---+---+---+---+---+---+---+---+---+---+---+ 131 | | Pointer | Length | Capacity | Vec (or String, PathBuf, ...) 132 | +---+---+---+---+---+---+---+---+---+---+---+---+ 133 | ^ 134 | | 135 | +---+---+---+---+ 136 | | Pointer | &Vec (or &String, &PathBuf, ...) 137 | +---+---+---+---+ 138 | ``` 139 | 140 | One advantage of taking `&[T]` instead of `&Vec` as an argument should be 141 | immediately apparent from the diagram: a `&[T]` has less indirection. 142 | 143 | However, there are other reasons: 144 | - Everything useful for `&Vec` is actually a method on `&[T]` 145 | - You can't check the capacity with a `&[T]`, but you can't change the capacity with a `&Vec` anyway 146 | - If you take `&[T]` as an argument, you can take shared slices that point to data 147 | which isn't owned by a `Vec` (such as static data, part of an array, into a `Box<[T]>`, et cetera) 148 | - So it is strictly and significantly more general to take `&[T]` 149 | 150 | Similar advantages apply to taking a `&str` instead of a `&String`, et cetera. 151 | 152 | In contrast, there are many things you can do with a `&mut Vec` that you can't 153 | do with a `&mut [T]`, so which you choose depends much more on what you need to 154 | do with the borrowed data. 155 | 156 | ### Graphical layout for arrays 157 | 158 | The layout of an array is the same as a slice, except the length is known. 159 | 160 | ``` 161 | +---+---+---+---+---+---+---+---+ 162 | | Pointer | Length | &[T] (or &str, &Path, Box<[T]>, ...) 163 | +---+---+---+---+---+---+---+---+ 164 | | 165 | V 166 | +---+---+---+---+---+---+---+ 167 | | D | A | T | A | . | . | . | [T; N] (or str, Path, ...) 168 | +---+---+---+---+---+---+---+ 169 | ^ 170 | | 171 | +---+---+---+---+ 172 | | Pointer | &[T; N] (or `Box<[T; N]>`, ...) 173 | +---+---+---+---+ 174 | ^ 175 | | 176 | +---+---+---+---+ 177 | | Pointer | &Box<[T; N]> (or &&[T; N], ...) 178 | +---+---+---+---+ 179 | ``` 180 | 181 | Because `[T; N]` is sized, and because the length is part of the type, 182 | pointers to it (like `&[T; N]`) are normal "thin" pointers, not "wide" 183 | pointers. But you can also create a `&[T]` that points to the array 184 | (or to part of the array), as in the diagram. 185 | 186 | Should you take a `&[T]` or a `&[T; N]` as a function argument? If 187 | you don't need a specific length, and aren't trying to generate code 188 | that's optimized based on the specific length of the array, you probably 189 | want `&[T]`. 190 | 191 | -------------------------------------------------------------------------------- /src/dyn-elision-advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced guidelines 2 | 3 | In this section, we cover how to guide elision behavior for your own 4 | generic data types, and point out some exceptions to the basic guidelines 5 | presented in the previous section. 6 | 7 | ## Guiding behavior of your own types 8 | 9 | When you declare a custom type with a lifetime parameter `'a` and a trait parameter `T: ?Sized`, 10 | including an *explicit* `T: 'a` bound will result in elision behaving the same as 11 | it does for references `&'a T` and for `std` types like `Ref<'a, T>`: 12 | ```rust 13 | // When `T` is replaced by `dyn Trait` with an elided lifetime, the elided lifetime 14 | // will default to `'a` outside of function bodies 15 | struct ExplicitOutlives<'a, T: 'a + ?Sized>(&'a T); 16 | ``` 17 | 18 | If your type has no lifetime parameter, or if there is no bound between the type 19 | parameter and the lifetime parameter, the default for elided `dyn Trait` lifetimes 20 | will be `'static`, like it is for `Box`. *This is true even if there is an 21 | **implied** `T: 'a` bound.* For example: 22 | ```rust,compile_fail 23 | trait Trait {} 24 | impl Trait for () {} 25 | 26 | // There's an *implied* `T: 'a` bound due to the `&'a T` field (RFC 2093) 27 | struct InferredOutlivesOnly<'a, T: ?Sized>(&'a T); 28 | 29 | // Yet this function expects an `InferredOutlivesOnly<'a, dyn Trait + 'static>` 30 | fn example<'a>(ioo: InferredOutlivesOnly<'a, dyn Trait>) {} 31 | 32 | // Thus this fails to compile 33 | fn attempt<'a>(ioo: InferredOutlivesOnly<'a, dyn Trait + 'a>) { 34 | example(ioo); 35 | } 36 | ``` 37 | 38 | If you make `T: 'a` explicit in the definition of the `struct`, the 39 | example will compile. 40 | 41 | If `T: 'a` is an inferred bound of your type, and `T: ?Sized`, I recommend 42 | including the explicit `T: 'a` bound. 43 | 44 | ## Ambiguous bounds 45 | 46 | If you have more than one lifetime bound in your type definition, the 47 | bound is considered ambiguous, even if one of the lifetimes is `'static` 48 | (or more generally, even if one lifetime is known to outlive the other). 49 | Such structs are rare, but if you have one, you usually must be explicit 50 | about the `dyn Trait` lifetime: 51 | ```rust,compile_fail 52 | # trait Trait {} 53 | # impl Trait for () {} 54 | struct S<'a, 'b: 'a, T: 'a + 'b + ?Sized>(&'a &'b T); 55 | 56 | // error[E0228]: the lifetime bound for this object type cannot be deduced 57 | // from context; please supply an explicit bound 58 | const C: S = S(&&()); 59 | ``` 60 | 61 | However, in function bodies, the lifetime is still inferred; moreover it 62 | is inferred independent of any annotation of the lifetime types: 63 | ```rust 64 | # trait Trait {} 65 | # impl Trait for () {} 66 | struct Weird<'a, 'b, T: 'a + 'b + ?Sized>(&'a T, &'b T); 67 | 68 | fn example<'a, 'b>() { 69 | // Either of `dyn Trait + 'a` or `dyn Trait + 'b` is an error, 70 | // so the `dyn Trait` lifetime must be inferred independently 71 | // from `'a` and `'b` 72 | let _: Weird<'a, 'b, dyn Trait> = Weird(&(), &()); 73 | } 74 | ``` 75 | 76 | (This is contrary to the documentation in the reference, and 77 | [ironically more flexible than non-ambiguous types.](#an-exception-to-inference-in-function-bodies) 78 | In this particular example, the lifetime will be inferred 79 | [analogously to the lifetime intersection mentioned previously.](dyn-trait-lifetime.md#when-multiple-lifetimes-are-involved)) 80 | 81 | ## Interaction with type aliases 82 | 83 | When you use a type alias, the bounds between lifetime parameters and type 84 | parameters *on the `type` alias* determine how `dyn Trait` lifetime elision 85 | behaves, overriding the bounds on the aliased type (be they stronger or weaker). 86 | 87 | ```rust,compile_fail 88 | # trait Trait {} 89 | // Without the `T: 'a` bound, the default trait object lifetime 90 | // for this alias is `'static` 91 | type MyRef<'a, T> = &'a T; 92 | 93 | // So this compiles 94 | fn foo(mr: MyRef<'_, dyn Trait>) -> &(dyn Trait + 'static) { 95 | mr 96 | } 97 | 98 | // With the `T: 'a` bound, the default trait object lifetime for 99 | // this alias is the lifetime parameter 100 | type MyOtherRef<'a, T: 'a> = MyRef<'a, T>; 101 | 102 | // So this does not compile 103 | fn bar(mr: MyOtherRef<'_, dyn Trait>) -> &(dyn Trait + 'static) { 104 | mr 105 | } 106 | ``` 107 | 108 | [See issue 100270.](https://github.com/rust-lang/rust/issues/100270) 109 | This is undocumented. 110 | 111 | ## Associated types and GATs 112 | 113 | `dyn Trait` lifetime elision applies in this context. There are some 114 | things of note, however: 115 | * Bounds on associated types and GATs don't seem to have any effect 116 | * Eliding non-`dyn Trait` lifetimes is not allowed 117 | 118 | For example: 119 | ```rust 120 | # trait Trait {} 121 | trait Assoc { 122 | type T: ?Sized; 123 | } 124 | 125 | impl Assoc for () { 126 | // dyn Trait + 'static 127 | type T = dyn Trait; 128 | } 129 | 130 | impl<'a> Assoc for &'a str { 131 | // &'a (dyn Trait + 'a) 132 | type T = &'a dyn Trait; 133 | // This is a compilation error as the reference lifetime is elided 134 | // type T = &dyn Trait; 135 | } 136 | ``` 137 | 138 | ```rust,compile_fail 139 | # trait Trait {} 140 | # impl Trait for () {} 141 | trait BoundedAssoc<'x> { 142 | type BA: 'x + ?Sized; 143 | } 144 | 145 | // Still `dyn Trait + 'static` 146 | impl<'x> BoundedAssoc<'x> for () { 147 | type BA = dyn Trait; 148 | } 149 | 150 | // Fails as `'a` might not be `'static` 151 | fn bib<'a>(obj: Box) { 152 | let obj: Box< <() as BoundedAssoc<'a>>::BA > = obj; 153 | } 154 | ``` 155 | 156 | ## An exception to inference in function bodies 157 | 158 | There is also an exception to the elided `dyn Trait` lifetime being inferred 159 | in function bodies. If you have a reference-like type, and you annotate the 160 | lifetime of the non-`dyn Trait` lifetime with a named lifetime, then the 161 | elided `dyn Trait` lifetime will be the same as the annotated lifetime 162 | (similar to how things behave outside of a function body): 163 | ```rust,compile_fail 164 | trait Trait {} 165 | impl Trait for () {} 166 | 167 | fn example<'a>(arg: &'a ()) { 168 | let dt: &'a dyn Trait = arg; 169 | // fails 170 | let _: &(dyn Trait + 'static) = dt; 171 | } 172 | ``` 173 | 174 | According to the reference, `&dyn Trait` should always behave like this. 175 | However, if the outer lifetime is elided or if `'_` is used for the outer lifetime, 176 | *the `dyn Trait` lifetime is inferred **independently** of the reference lifetime:* 177 | ```rust 178 | # trait Trait {} 179 | # impl Trait for () {} 180 | fn example() { 181 | let local = (); 182 | 183 | // The outer reference lifetime cannot be `'static`... 184 | let obj: &dyn Trait = &local; 185 | 186 | // Yet the `dyn Trait` lifetime is! 187 | let _: &(dyn Trait + 'static) = obj; 188 | } 189 | ``` 190 | 191 | This is not documented anywhere and is in conflict with the reference. 192 | [It was implemented here,](https://github.com/rust-lang/rust/pull/39305) 193 | with no team input or FCP. 🤷 194 | 195 | However, the chances that you will run into a problem due to this behavior 196 | is low, as it's rare to annotate lifetimes within a function body. 197 | 198 | ## What we are not yet covering 199 | 200 | To the best of our knowledge, this covers the behavior of `dyn Trait` lifetime elision 201 | *when there are no lifetime bounds on the trait itself.* Non-`'static` lifetime bounds 202 | on the trait itself lead to some more nuanced behavior; we'll cover some of them in the 203 | [next section.](./dyn-elision-trait-bounds.md) 204 | 205 | ## Advanced guidelines summary 206 | 207 | So all in all, we have three common categories of `dyn Trait` lifetime elision 208 | when ignoring lifetime bounds on traits: 209 | 210 | - `'static` for type parameters with no (explicit) lifetime bound 211 | - E.g. `Box` (`Box`) 212 | - E.g. `struct Unbounded<'a, T: ?Sized>(&'a T)` 213 | - Another lifetime parameter for type parameters with a single (explicit) lifetime bound 214 | - E.g. `&'a dyn Trait` (`&'a dyn Trait + 'a`) 215 | - E.g. `Ref<'a, dyn Trait>` (`Ref<'a, dyn Trait + 'a>`) 216 | - E.g. `struct Bounded<'a, T: 'a + ?Sized>(&'a T)` 217 | - Ambiguous due to multiple bounds (rare) 218 | - E.g. `struct Weird<'a, T: 'a + 'static>(&'a T);` 219 | 220 | And the behavior in various contexts is: 221 | 222 | | | `static` | `impl` | \[G\]AT | `fn` in | `fn` out | `fn` body | 223 | | - | - | - | - | - | - | - | 224 | | `Box` | `'static` | `'static` | `'static` | `'static` | `'static` | Inferred 225 | | `&dyn Trait` | Ref | Ref | E0637 | Ref | Ref | Inferred 226 | | `&'a dyn Trait` | Ref | Ref | Ref | Ref | Ref | Ref 227 | | Ambig. | E0228 | E0228 | E0228 | E0228 | E0228 | Inferred 228 | 229 | With the following notes: 230 | - `type` alias bounds take precedence over the aliased type bounds 231 | - Associated type and GAT bounds do not effect the default 232 | 233 | For contrast, the "normal" elision rules work like so: 234 | 235 | | | `static` | `impl` | \[G\]AT | `fn` in | `fn` out | `fn` body | 236 | | - | - | - | - | - | - | - | 237 | | `Box` | `'static` | Fresh | E0637 | Fresh | Elision | Inferred 238 | | `&(dyn Trait + '_`) | `'static` | Fresh | E0637 | Fresh | Elision | Inferred 239 | | `&'a (dyn Trait + '_`) | `'static` | Fresh | E0637 | Fresh | Elision | Inferred 240 | | Ambig. with `'_` | `'static` | Fresh | E0637 | Fresh | Elision | Inferred 241 | 242 | 243 | -------------------------------------------------------------------------------- /src/dyn-trait-borrow.md: -------------------------------------------------------------------------------- 1 | # Generalizing borrows 2 | 3 | A lot of core traits are built around some sort of *field projection,* where 4 | the implementing type contains some other type `T` and you can convert a 5 | `&self` to a `&T` or `&mut self` to a `&mut T`. 6 | ```rust 7 | pub trait Deref { 8 | type Target: ?Sized; 9 | fn deref(&self) -> &Self::Target; 10 | } 11 | 12 | pub trait Index { 13 | type Output: ?Sized; 14 | fn index(&self, index: Idx) -> &Self::Output; 15 | } 16 | 17 | pub trait AsRef { 18 | fn as_ref(&self) -> &T; 19 | } 20 | 21 | pub trait Borrow { 22 | fn borrow(&self) -> &Borrowed; 23 | } 24 | 25 | // `DerefMut`, `IndexMut`, `AsMut`, ... 26 | ``` 27 | 28 | There's generally no way to implement these traits if the type you want to 29 | return is not contained within `Self` (except for returning a reference to 30 | some static value or similar, which is rarely what you want). 31 | 32 | However, sometimes you have a custom borrowing type which is *not* 33 | actually contained within your owning type: 34 | ```rust 35 | // We wish we could implement `Borrow>`, but we can't 36 | pub struct Data { 37 | first: usize, 38 | others: Vec, 39 | } 40 | 41 | pub struct DataRef<'a> { 42 | first: usize, 43 | others: &'a [usize], 44 | } 45 | 46 | pub struct DataMut<'a> { 47 | first: usize, 48 | others: &'a mut Vec, 49 | } 50 | ``` 51 | 52 | This can be problematic when interacting with libraries and data structures 53 | such as [`std::collections::HashSet`,](https://doc.rust-lang.org/std/collections/struct.HashSet.html) 54 | which [rely on the `Borrow` trait](https://doc.rust-lang.org/std/collections/struct.HashSet.html#method.contains) 55 | to be able to look up entries without taking ownership. 56 | 57 | One way around this problem is to use 58 | [a different library or type which is more flexible.](https://docs.rs/hashbrown/0.14.0/hashbrown/struct.HashSet.html#method.contains) 59 | However, it's also possible to tackle the problem with a bit of indirection and type erasure. 60 | 61 | ## Your types contain a borrower 62 | 63 | Here we present a solution to the problem by 64 | [Eric Michael Sumner](https://orcid.org/0000-0002-6439-9757), who has 65 | graciously blessed its inclusion in this guide. I've rewritten the 66 | original for the sake of presentation, and any errors are my own. 67 | 68 | The main idea behind the approach is to utilize the following trait, which 69 | encapsulates the ability to borrow `self` in the form of your custom borrowed 70 | type: 71 | ```rust 72 | #pub struct Data { first: usize, others: Vec } 73 | #pub struct DataRef<'a> { first: usize, others: &'a [usize] } 74 | pub trait Lend { 75 | fn lend(&self) -> DataRef<'_>; 76 | } 77 | 78 | impl Lend for Data { 79 | fn lend(&self) -> DataRef<'_> { 80 | DataRef { 81 | first: self.first, 82 | others: &self.others, 83 | } 84 | } 85 | } 86 | 87 | impl Lend for DataRef<'_> { 88 | fn lend(&self) -> DataRef<'_> { 89 | DataRef { 90 | first: self.first, 91 | others: self.others, 92 | } 93 | } 94 | } 95 | 96 | // impl Lend for DataMut<'_> ... 97 | ``` 98 | 99 | And the key insight is that any implementor can also coerce from 100 | `&self` to `&dyn Lend`. We can therefore implement traits like 101 | `Borrow`, because every implementor "contains" a `dyn Lend` -- 102 | themselves! 103 | 104 | ```rust 105 | #pub struct Data { first: usize, others: Vec } 106 | #pub struct DataRef<'a> { first: usize, others: &'a [usize] } 107 | #pub trait Lend { fn lend(&self) -> DataRef<'_>; } 108 | #impl Lend for Data { 109 | # fn lend(&self) -> DataRef<'_> { 110 | # DataRef { 111 | # first: self.first, 112 | # others: &self.others, 113 | # } 114 | # } 115 | #} 116 | #impl Lend for DataRef<'_> { 117 | # fn lend(&self) -> DataRef<'_> { 118 | # DataRef { 119 | # first: self.first, 120 | # others: self.others, 121 | # } 122 | # } 123 | #} 124 | use std::borrow::Borrow; 125 | 126 | impl<'a> Borrow for Data { 127 | fn borrow(&self) -> &(dyn Lend + 'a) { self } 128 | } 129 | 130 | impl<'a, 'b: 'a> Borrow for DataRef<'b> { 131 | fn borrow(&self) -> &(dyn Lend + 'a) { self } 132 | } 133 | 134 | // impl<'a, 'b: 'a> Borrow for DataMut<'b> ... 135 | ``` 136 | 137 | This gives us a common `Borrow` type for both our owning and 138 | custom borrowing data structures. To look up borrowed entries 139 | in a `HashSet`, for example, we can cast a `&DataRef<'_>` to 140 | a `&dyn Lend` and pass that to `set.contains`; the `HashSet` can 141 | hash the `dyn Lend` and then borrow the owned `Data` entries as 142 | `dyn Lend` as well, in order to do the necessary lookup 143 | comparisons. 144 | 145 | That means we need to implement the requisite functionality such as 146 | `PartialEq` and `Hash` for `dyn Lend`. But this is a different use case 147 | than [our general solution in the previous section.](./dyn-trait-eq.md) 148 | In that case we wanted `PartialEq` for our already-type-erased `dyn Trait`, 149 | so we could compare values across any arbitrary implementing types. 150 | 151 | Here we don't care about arbitrary types, and we also have the ability 152 | to produce a concrete type that references our actual data. We can 153 | use that to implement the functionality; there's no need for downcasting 154 | or any of that in order to implement the requisite traits for `dyn Lend`. 155 | We don't really *care* that `dyn Lend` will implement `PartialEq` and 156 | `Hash` per se, as that is just a means to an end: giving `HashSet` and 157 | friends a way to compare our custom concrete borrowing types despite the 158 | `Borrow` trait bound. 159 | 160 | First things first though, we need our concrete types to implement 161 | the requisite traits themselves. The main thing to be mindful of 162 | is that we maintain 163 | [the invariants expected by `Borrow`.](https://doc.rust-lang.org/std/borrow/trait.Borrow.html) 164 | For this example, we're lucky enough that our borrowing 165 | type can just derive all of the requisite functionality: 166 | ```rust 167 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 168 | pub struct DataRef<'a> { 169 | first: usize, 170 | others: &'a [usize], 171 | } 172 | 173 | #[derive(Debug, Clone)] 174 | pub struct Data { 175 | first: usize, 176 | others: Vec, 177 | } 178 | 179 | #[derive(Debug)] 180 | pub struct DataMut<'a> { 181 | first: usize, 182 | others: &'a mut Vec, 183 | } 184 | ``` 185 | However, we haven't derived the traits that are semantically important 186 | to `Borrow` for our other types. We technically could have in 187 | this case, because 188 | - our fields are in the same order as they are in the borrowed type 189 | - every field is present 190 | - every field has a `Borrow` relationship when comparing with the borrowed type's field 191 | - we understand how the `derive` works 192 | 193 | But all those things might not be true for your use case, and even 194 | when they are, relying on them creates a very fragile arrangement. 195 | It's just too easy to accidentally break things by adding a field 196 | or even just rearranging the field order. 197 | 198 | Instead, we implement the traits directly by deferring to the borrowed type: 199 | ```rust 200 | # struct Data; impl Data { fn lend(&self) {} } 201 | // Exercise for the reader: `PartialEq` across all of our 202 | // owned and borrowed types :-) 203 | impl std::cmp::PartialEq for Data { 204 | fn eq(&self, other: &Self) -> bool { 205 | self.lend() == other.lend() 206 | } 207 | } 208 | 209 | impl std::cmp::Eq for Data {} 210 | 211 | impl std::hash::Hash for Data { 212 | fn hash(&self, hasher: &mut H) { 213 | self.lend().hash(hasher) 214 | } 215 | } 216 | 217 | // Similarly for `DataMut<'_>` 218 | ``` 219 | And in fact, this is exactly the approach we want to take for 220 | `dyn Lend` as well: 221 | ```rust 222 | #pub struct DataRef<'a> { first: usize, others: &'a [usize] } 223 | #pub trait Lend { fn lend(&self); } 224 | impl std::cmp::PartialEq for dyn Lend + '_ { 225 | fn eq(&self, other: &(dyn Lend + '_)) -> bool { 226 | self.lend() == other.lend() 227 | } 228 | } 229 | 230 | impl std::cmp::Eq for dyn Lend + '_ {} 231 | 232 | impl std::hash::Hash for dyn Lend + '_ { 233 | fn hash(&self, hasher: &mut H) { 234 | self.lend().hash(hasher) 235 | } 236 | } 237 | ``` 238 | 239 | Whew, that was a lot of boilerplate. But we're finally at a 240 | place where we can store `Data` in a `HashSet` and look up 241 | entries when we only have a `DataRef`: 242 | ```rust,ignore 243 | use std::collections::HashSet; 244 | let set = [ 245 | Data { first: 3, others: vec![5,7]}, 246 | ].into_iter().collect::>(); 247 | 248 | assert!(set.contains::(&DataRef { first: 3, others: &[5,7]})) 249 | 250 | // Alternative to turbofishing 251 | let data_ref = DataRef { first: 3, others: &[5,7]}; 252 | assert!(set.contains(&data_ref as &dyn Lend)); 253 | ``` 254 | 255 | [Here's a playground with the complete example.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cae538dbbf73e3e7692135bf2d397f39) 256 | 257 | Another alternative to casting or turbofish is to add an 258 | `as_lend(&self) -> &dyn Lend + '_` method, similar to many 259 | of the previous examples. 260 | -------------------------------------------------------------------------------- /src/lt-ex-mut-slice.md: -------------------------------------------------------------------------------- 1 | # Mutable slice iterator 2 | 3 | The standard library has [an iterator over `&mut [T]`](https://doc.rust-lang.org/std/slice/struct.IterMut.html) 4 | which is implemented (as of this writing) in terms of pointer arithmetic, presumably for the sake of optimization. 5 | In this example, we'll show how one can implement their own mutable slice iterator with entirely safe code. 6 | 7 | Here's the starting place for our implementation: 8 | ```rust 9 | struct MyIterMut<'a, T> { 10 | slice: &'a mut [T], 11 | // ...maybe other fields for your needs... 12 | } 13 | 14 | impl<'a, T> Iterator for MyIterMut<'a, T> { 15 | type Item = &'a mut T; 16 | fn next(&mut self) -> Option { 17 | todo!() 18 | } 19 | } 20 | ``` 21 | Below are a few starting attempts at it. Spoilers, they don't compile. 22 | ```rust,compile_fail 23 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 24 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 25 | # type Item = &'a mut T; 26 | fn next(&mut self) -> Option { 27 | // Eh, we'll worry about iterative logic later! 28 | self.slice.get_mut(0) 29 | } 30 | #} 31 | ``` 32 | ```rust,compile_fail 33 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 34 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 35 | # type Item = &'a mut T; 36 | fn next(&mut self) -> Option { 37 | // Actually, this method looks perfect for our iteration logic 38 | let (first, rest) = self.slice.split_first_mut()?; 39 | self.slice = rest; 40 | Some(first) 41 | } 42 | #} 43 | ``` 44 | ```rust,compile_fail 45 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 46 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 47 | # type Item = &'a mut T; 48 | fn next(&mut self) -> Option { 49 | // 🤔 Pattern matching?? 50 | match &mut self.slice { 51 | [] => None, 52 | [first, rest @ ..] => Some(first), 53 | } 54 | } 55 | #} 56 | ``` 57 | 58 | Yeah, the compiler really doesn't like any of that. Let's take a minute to write out all the elided 59 | lifetimes. Some of them are in aliases, which we're also going to expand: 60 | - `Item` is `&'a mut T` 61 | - `&mut self` is short for `self: &mut Self`, and 62 | - `Self` is `MyIterMut<'a, T>` 63 | 64 | Here's what it looks like with everything being explicit: 65 | ```rust 66 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 67 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 68 | # type Item = &'a mut T; 69 | fn next<'s>(self: &'s mut MyIterMut<'a, T>) -> Option<&'a mut T> { 70 | todo!() 71 | } 72 | #} 73 | ``` 74 | And remember that in `MyIterMut<'a, T>`, `slice` is a `&'a mut [T]`. 75 | 76 | Ah, yes. [We have a nested exclusive borrow](./st-invariance.md) here. 77 | 78 | > You cannot get a `&'long mut U` through dereferencing a `&'short mut &'long mut U`. 79 | > - You can only reborrow a `&'short mut U`. 80 | 81 | There is no safe way to go *through* the `&'s mut self` and pull out a `&'a mut T`. 82 | 83 | Are we stuck then? No, there is actually a way forward! As it turns out, 84 | slices are special. In particular, the compiler understands that an *empty* slice 85 | covers *no actual data*, so there can't be any memory aliasing concerns or data races, 86 | et cetera. So the compiler understands it's perfectly sound to pull an empty slice 87 | reference out of no where, with any lifetime at all. Even if it's an exclusive slice reference! 88 | ```rust 89 | fn magic() -> &'static mut [T] { 90 | &mut [] 91 | } 92 | ``` 93 | 94 | For our purposes, we don't even need the magic: the standard library 95 | [has a `Default` implementation](https://doc.rust-lang.org/std/default/trait.Default.html#impl-Default-for-%26mut+%5BT%5D) 96 | for `&mut [T]`. 97 | 98 | Why does this unstick us? With that implementation, we can conjure an empty `&mut [T]` 99 | out of nowhere and *move our `slice` field out from behind `&mut self`:* 100 | ```rust 101 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 102 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 103 | # type Item = &'a mut T; 104 | fn next(&mut self) -> Option { 105 | let mut slice = std::mem::take(&mut self.slice); 106 | // Eh, we'll worry about iterative logic later! 107 | slice.get_mut(0) 108 | } 109 | #} 110 | ``` 111 | [`std::mem::take`](https://doc.rust-lang.org/std/mem/fn.take.html) and `swap` and `replace` are 112 | very useful and safe functions; don't be thrown off by them being in `std::mem` along side the 113 | dangerous `transmute` and other low-level functions. Note how we passed `&mut self.slice` -- 114 | that's a `&mut &mut [T]`. `take` replaces everything inside of the outer `&mut`, which can have 115 | an arbitrarily short lifetime -- just long enough to move the memory around. 116 | 117 | So we're done aside from iterative logic, right? This should just give us the first element forever? 118 | ```rust 119 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 120 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 121 | # type Item = &'a mut T; 122 | # fn next(&mut self) -> Option { 123 | # let mut slice = std::mem::take(&mut self.slice); 124 | # slice.get_mut(0) 125 | # } 126 | #} 127 | let mut arr = [0, 1, 2, 3]; 128 | let iter = MyIterMut { slice: &mut arr }; 129 | for x in iter.take(10) { 130 | println!("{x}"); 131 | } 132 | ``` 133 | Uh, it only gave us one item. Oh right -- when we're done with the slice, we need to move it 134 | back into our `slice` field. We only want to *temporarily* replace that field with an empty slice. 135 | ```rust,compile_fail 136 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 137 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 138 | # type Item = &'a mut T; 139 | fn next(&mut self) -> Option { 140 | let mut slice = std::mem::take(&mut self.slice); 141 | // Eh, we'll worry about iterative logic later! 142 | let first = slice.get_mut(0); 143 | self.slice = slice; 144 | first 145 | } 146 | #} 147 | ``` 148 | Uh oh, now what. 149 | ```rust 150 | 151 | error[E0499]: cannot borrow `*slice` as mutable more than once at a time 152 | 9 | let first = slice.get_mut(0); 153 | | ----- first mutable borrow occurs here 154 | 10 | self.slice = slice; 155 | | ^^^^^ second mutable borrow occurs here 156 | 11 | first 157 | | ----- returning this value requires that `*slice` is borrowed for `'a` 158 | ``` 159 | Oh, right! These are *exclusive* references. We can't return the same item multiple 160 | times -- that would mean someone could get multiple `&mut` to the same element if they 161 | [collect](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect)ed the 162 | iterator, for example. Come to think of it, we can't punt on our iteration logic 163 | either -- if we try to hold on to the entire `&mut [T]` while handing out `&mut T` 164 | to the elements, that's *also* multiple `&mut` to the same memory! 165 | 166 | This is what the error is telling us: We can't hold onto the entire `slice` and 167 | return `first`. 168 | 169 | (There's a pattern called "lending iterators" where you can hand out borrows of 170 | data you own in an iterator-like fashion, but it's not possible with the current 171 | `Iterator` trait; it is also a topic for another day.) 172 | 173 | Alright, let's try [`split_first_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_first_mut) 174 | again instead, that really did seem like a perfect fit for our iteration logic. 175 | ```rust 176 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 177 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 178 | # type Item = &'a mut T; 179 | fn next(&mut self) -> Option { 180 | let mut slice = std::mem::take(&mut self.slice); 181 | let (first, rest) = slice.split_first_mut()?; 182 | self.slice = rest; 183 | Some(first) 184 | } 185 | # } 186 | 187 | // ... 188 | 189 | let mut arr = [0, 1, 2, 3]; 190 | let iter = MyIterMut { slice: &mut arr }; 191 | for x in iter { 192 | println!("{x}"); 193 | } 194 | ``` 195 | 196 | Finally, a working version! `split_first_mut` is a form of [borrow splitting,](./lifetime-analysis.md#independently-borrowing-fields) 197 | which we briefly mentioned before. 198 | 199 | And for the sake of completion, here's the pattern based approach to borrow splitting: 200 | ```rust 201 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 202 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 203 | # type Item = &'a mut T; 204 | fn next(&mut self) -> Option { 205 | // ....these are all that changed.... 206 | // vvvvvvvvvvvvvvvvvv v 207 | match std::mem::take(&mut self.slice) { 208 | [] => None, 209 | [first, rest @ ..] => Some(first), 210 | } 211 | } 212 | # } 213 | 214 | // ... 215 | 216 | let mut arr = [0, 1, 2, 3]; 217 | let iter = MyIterMut { slice: &mut arr }; 218 | for x in iter { 219 | println!("{x}"); 220 | } 221 | ``` 222 | Oh whoops, just one element again. Right. We need to put the `rest` back in `self.slice`: 223 | ```rust 224 | #struct MyIterMut<'a, T> { slice: &'a mut [T], } 225 | # impl<'a, T> Iterator for MyIterMut<'a, T> { 226 | # type Item = &'a mut T; 227 | fn next(&mut self) -> Option { 228 | match std::mem::take(&mut self.slice) { 229 | [] => None, 230 | [first, rest @ ..] => { 231 | self.slice = rest; 232 | Some(first) 233 | } 234 | } 235 | } 236 | # } 237 | 238 | // ... 239 | 240 | let mut arr = [0, 1, 2, 3]; 241 | let iter = MyIterMut { slice: &mut arr }; 242 | for x in iter { 243 | println!("{x}"); 244 | } 245 | ``` 246 | 👍 247 | 248 | -------------------------------------------------------------------------------- /src/lifetime-analysis.md: -------------------------------------------------------------------------------- 1 | # Understand borrows within a function 2 | 3 | The analysis that the compiler does to determine lifetimes and borrow check 4 | *within* a function body is quite complicated. A full exploration is beyond 5 | the scope of this guide, but we'll give a brief introduction here. 6 | 7 | Your best bet if you run into an error you can't understand is to 8 | ask for help on the forum or elsewhere. 9 | 10 | ## Borrow errors within a function 11 | 12 | Here are some simple causes of borrow check errors within a function. 13 | 14 | ### Recalling the Basics 15 | 16 | The most basic mechanism to keep in mind is that `&mut` references are exclusive, 17 | while `&` references are shared and implement `Copy`. You can't intermix using 18 | a shared reference and an exclusive reference to the same value, or two exclusive 19 | references to the same value. 20 | 21 | ```rust 22 | # fn main() { 23 | let mut local = "Hello".to_string(); 24 | 25 | // Creating and using a shared reference 26 | let x = &local; 27 | println!("{x}"); 28 | 29 | // Creating and using an exclusive reference 30 | let y = &mut local; 31 | y.push_str(", world!"); 32 | 33 | // Trying to use the shared reference again 34 | println!("{x}"); 35 | # } 36 | ``` 37 | 38 | This doesn't compile because as soon as you created the exclusive reference, 39 | any other existing references must cease to be valid. 40 | 41 | ### Borrows are often implicit 42 | 43 | Here's the example again, only slightly rewritten. 44 | ```rust 45 | # fn main() { 46 | let mut local = "Hello".to_string(); 47 | 48 | // Creating and using a shared reference 49 | let x = &local; 50 | println!("{x}"); 51 | 52 | // Implicitly creating and using an exclusive reference 53 | local.push_str(", world!"); 54 | 55 | // Trying to use the shared reference again 56 | println!("{x}"); 57 | # } 58 | ``` 59 | Here, [`push_str` takes `&mut self`,](https://doc.rust-lang.org/std/string/struct.String.html#method.push_str) 60 | so an implicit `&mut local` exists as part of the method call, 61 | and thus the example can still not compile. 62 | 63 | ### Creating a `&mut` is not the only exclusive use 64 | 65 | The borrow checker looks at *every* use of a value to see if it's 66 | compatible with the lifetimes of borrows to that value, not 67 | just uses that involve references or just uses that involve lifetimes. 68 | 69 | For example, moving a value invalidates any references to the 70 | value, as otherwise those references would dangle. 71 | 72 | ```rust 73 | # fn main() { 74 | let local = "Hello".to_string(); 75 | 76 | // Creating and using a shared reference 77 | let x = &local; 78 | println!("{x}"); 79 | 80 | // Moving the value 81 | let _local = local; 82 | 83 | // Trying to use the shared reference again 84 | println!("{x}"); 85 | # } 86 | ``` 87 | 88 | ### Referenced values must remain in scope 89 | 90 | The effects of a value going out of scope are similar to moving the 91 | value: all references are invalidated. 92 | ```rust 93 | # fn main() { 94 | let x; 95 | { 96 | let local = "Hello".to_string(); 97 | x = &local; 98 | } // `local` goes out of scope here 99 | 100 | // Trying to use the shared reference after `local` goes out of scope 101 | println!("{x}"); 102 | # } 103 | ``` 104 | 105 | ### Using `&mut self` or `&self` counts as a use of all fields 106 | 107 | In the example below, `left` becomes invalid when we create `&self` 108 | to call `bar`. Because you can get a `&self.left` out of a `&self`, 109 | this is similar to trying to intermix `&mut self.left` and `&self.left`. 110 | 111 | ```rust 112 | #[derive(Debug)] 113 | struct Pair { 114 | left: String, 115 | right: String, 116 | } 117 | 118 | impl Pair { 119 | fn foo(&mut self) { 120 | let left = &mut self.left; 121 | left.push_str("hi"); 122 | self.bar(); 123 | println!("{left}"); 124 | } 125 | fn bar(&self) { 126 | println!("{self:?}"); 127 | } 128 | } 129 | ``` 130 | 131 | More generally, creating a `&mut x` or `&x` counts as a use of 132 | everything reachable from `x`. 133 | 134 | ## Some things that compile successfully 135 | 136 | Once you've started to get the hang of borrow errors, you might start to 137 | wonder why certain programs are *allowed* to compile. Here we introduce 138 | some of the ways that Rust allows non-trivial borrowing while still being 139 | sound. 140 | 141 | ### Independently borrowing fields 142 | 143 | Rust tracks borrows of struct fields individually, so the borrows of 144 | `left` and `right` below do not conflict. 145 | 146 | ```rust 147 | # #[derive(Debug)] 148 | # struct Pair { 149 | # left: String, 150 | # right: String, 151 | # } 152 | # 153 | impl Pair { 154 | fn foo(&mut self) { 155 | let left = &mut self.left; 156 | let right = &mut self.right; 157 | left.push_str("hi"); 158 | right.push_str("there"); 159 | println!("{left} {right}"); 160 | } 161 | } 162 | ``` 163 | 164 | This capability is also called [splitting borrows.](https://doc.rust-lang.org/nomicon/borrow-splitting.html) 165 | 166 | Note that data you access through indexing are not consider fields 167 | per se; instead indexing is [an operation that generally borrows 168 | all of `&self` or `&mut self`.](https://doc.rust-lang.org/std/ops/trait.Index.html) 169 | 170 | ```rust 171 | # fn main() { 172 | let mut v = vec![0, 1, 2]; 173 | 174 | // These two do not overlap, but... 175 | let left = &mut v[..1]; 176 | let right = &mut v[1..]; 177 | 178 | // ...the borrow checker cannot recognize that 179 | println!("{left:?} {right:?}"); 180 | # } 181 | ``` 182 | 183 | Usually in this case, one uses methods like [`split_at_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut) 184 | in order to split the borrows instead. 185 | 186 | Similarly to indexing, when you access something through "deref coercion", you're 187 | exercising [the `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html) 188 | (or `DerefMut`), which borrow all of `self`. 189 | 190 | There are also some niche cases where the borrow checker is smarter, however. 191 | ```rust 192 | # fn main() { 193 | // Pattern matching does understand non-overlapping slices (slices are special) 194 | let mut v = vec![String::new(), String::new()]; 195 | let slice = &mut v[..]; 196 | if let [_left, right] = slice { 197 | if let [left, ..] = slice { 198 | left.push_str("left"); 199 | } 200 | // Still usable! 201 | right.push_str("right"); 202 | } 203 | # } 204 | ``` 205 | ```rust 206 | # fn main() { 207 | // You can split borrows through a `Box` dereference (`Box` is special) 208 | let mut bx = Box::new((0, 1)); 209 | let left = &mut bx.0; 210 | let right = &mut bx.1; 211 | *left += 1; 212 | *right += 1; 213 | # } 214 | ``` 215 | 216 | The examples are non-exhaustive 🙂. 217 | 218 | ### Reborrowing 219 | 220 | [As mentioned before,](./st-reborrow.md) reborrows are what make `&mut` reasonable to use. 221 | In fact, they have other special properties you can't emulate with a custom struct and 222 | trait implementations. Consider this example: 223 | ```rust 224 | fn foo(s: &mut String) -> &str { 225 | &**s 226 | } 227 | ``` 228 | Actually, that's too fast. Let's change this a little bit and go step by step. 229 | ```rust 230 | fn foo(s: &mut String) -> &str { 231 | let ms: &mut str = &mut **s; 232 | let rs: &str = &*s; 233 | rs 234 | } 235 | ``` 236 | Here, both `s` and `ms` are going out of scope at the end of `foo`, but this doesn't 237 | invalidate `rs`. That is, reborrowing through references can impose lifetime constraints 238 | on the reborrow, but the reborrow is not dependent on references staying in scope! It is 239 | only dependent on the borrowed data. 240 | 241 | This demonstrates that reborrowing is more powerful than nesting references. 242 | 243 | ### Shared reborrowing 244 | 245 | When it comes to detecting conflicts, the borrow checker distinguishes between shared 246 | reborrows and exclusive ones. In particular, creating a shared reborrow will invalidate 247 | any exclusive reborrows of the same value (as they are no longer exclusive). But it will 248 | not invalidated shared reborrows: 249 | 250 | ```rust 251 | struct Pair { 252 | left: String, 253 | right: String, 254 | } 255 | 256 | impl Pair { 257 | fn foo(&mut self) { 258 | // a split borrow: exclusive reborrow, shared reborrow 259 | let left = &mut self.left; 260 | let right = &self.right; 261 | left.push('x'); 262 | 263 | // Shared reborrow of all of `self`, which "covers" all fields 264 | let this = &*self; 265 | 266 | // It invalidates any exclusive reborrows, so this will fail... 267 | // println!("{left}"); 268 | 269 | // But it does not invalidate shared reborrows! 270 | println!("{right}"); 271 | } 272 | } 273 | ``` 274 | 275 | ### Two-phase borrows 276 | 277 | The following code compiles: 278 | ```rust 279 | # fn main() { 280 | let mut v = vec![0]; 281 | let shared = &v; 282 | v.push(shared.len()); 283 | # } 284 | ``` 285 | 286 | However, if you're aware of the order of evaluation here, it probably seems like it shouldn't. 287 | The implicit `&mut v` should have invalidated `shared` before `shared.len()` was evaluated. 288 | What gives? 289 | 290 | This is the result of a feature called two-phase borrows, which is intended to make 291 | [nested method calls](https://rust-lang.github.io/rfcs/2025-nested-method-calls.html) 292 | more ergonomic: 293 | ```rust 294 | # fn main() { 295 | let mut v = vec![0]; 296 | v.push(v.len()); 297 | # } 298 | ``` 299 | [In the olden days,](https://rust.godbolt.org/z/966j4Eh1f) you would have had to write it like so: 300 | ```rust 301 | # fn main() { 302 | let mut v = vec![0]; 303 | let len = v.len(); 304 | v.push(len); 305 | # } 306 | ``` 307 | 308 | The implementation slipped, which is why the first example compiles too. How far it slipped 309 | is hard to say, as not only is there [no specification,](https://github.com/rust-lang/rust/issues/46901) 310 | the feature doesn't even seem to be documented 🤷. 311 | 312 | -------------------------------------------------------------------------------- /src/dyn-hr.md: -------------------------------------------------------------------------------- 1 | # Higher-ranked types 2 | 3 | Another feature of trait objects is that they can be *higher-ranked* over 4 | lifetime parameters of the trait: 5 | ```rust 6 | // A trait with a lifetime parameter 7 | trait Look<'s> { 8 | fn method(&self, s: &'s str); 9 | } 10 | 11 | // An implementation that works for any lifetime 12 | impl<'s> Look<'s> for () { 13 | fn method(&self, s: &'s str) { 14 | println!("Hi there, {s}!"); 15 | } 16 | } 17 | 18 | fn main() { 19 | // A higher-ranked trait object 20 | // vvvvvvvvvvvvvvvvvvvvvvvv 21 | let _bx: Box Look<'any>> = Box::new(()); 22 | } 23 | ``` 24 | The `for<'x>` part is a *lifetime binder* that introduces higher-ranked 25 | lifetimes. There can be more than one lifetime, and you can give them 26 | arbitrary names just like lifetime parameters on functions, structs, 27 | and so on. 28 | 29 | You can only coerce to a higher-ranked trait object if you implement 30 | the trait in question for *all* lifetimes. For example, this doesn't 31 | work: 32 | ```rust,compile_fail 33 | # trait Look<'s> { fn method(&self, s: &'s str); } 34 | impl<'s> Look<'s> for &'s i32 { 35 | fn method(&self, s: &'s str) { 36 | println!("Hi there, {s}!"); 37 | } 38 | } 39 | 40 | fn main() { 41 | let _bx: Box Look<'any>> = Box::new(&0); 42 | } 43 | ``` 44 | `&'s i32` only implements `Look<'s>`, not `Look<'a>` for all lifetimes `'a`. 45 | 46 | Similarly, this won't work either: 47 | ```rust,compile_fail 48 | # trait Look<'s> { fn method(&self, s: &'s str); } 49 | impl Look<'static> for i32 { 50 | fn method(&self, s: &'static str) { 51 | println!("Hi there, {s}!"); 52 | } 53 | } 54 | 55 | fn main() { 56 | let _bx: Box Look<'any>> = Box::new(0); 57 | } 58 | ``` 59 | 60 | Implementing the trait with `'static` as the lifetime parameter is not the 61 | same thing as implementing the trait for any lifetime as the parameter. 62 | Traits and trait implementations don't have something like variance; the 63 | parameters of traits are always invariant and thus implementations are 64 | always for the explicit lifetime(s) only. 65 | 66 | ## Subtyping 67 | 68 | There's a relationship between higher-ranked types like `dyn for<'any> Look<'any>` 69 | and non-higher-ranked types like `dyn Look<'x>` (for a single lifetime `'x`): the 70 | higher-ranked type is a subtype of the non-higher-ranked types. Thus you can 71 | coerce a higher-ranked type to a non-higher-ranked type with any concrete lifetime: 72 | ```rust 73 | # trait Look<'s> { fn method(&self, s: &'s str); } 74 | fn as_static(bx: Box Look<'any>>) -> Box> { 75 | bx 76 | } 77 | 78 | fn as_whatever<'w>(bx: Box Look<'any>>) -> Box> { 79 | bx 80 | } 81 | ``` 82 | 83 | Note that this still isn't a form of variance for the *lifetime parameter* of the 84 | trait. This fails for example, because you can't coerce from `dyn Look<'static>` 85 | to `dyn Look<'w>`: 86 | ```rust 87 | # trait Look<'s> { fn method(&self, s: &'s str); } 88 | # fn as_static(bx: Box Look<'any>>) -> Box> { bx } 89 | fn as_whatever<'w>(bx: Box Look<'any>>) -> Box> { 90 | as_static(bx) 91 | } 92 | ``` 93 | 94 | As a supertype coercion, going from higher-ranked to non-higher-ranked can 95 | apply even in a covariant nested context, 96 | [just like non-higher-ranked supertype coercions:](./dyn-covariance.md#variance-in-nested-context) 97 | ```rust 98 | # trait Look<'s> {} 99 | fn foo<'l: 's, 's, 'p>( 100 | v: Vec Look<'any> + 'l>> 101 | ) -> Vec + 's>> 102 | { 103 | v 104 | } 105 | ``` 106 | 107 | ## `Fn` traits and `fn` pointers 108 | 109 | The `Fn` traits ([`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html), 110 | [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), 111 | and [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html)) 112 | have special-cased syntax. For one, you write them out to look more like 113 | a function, using `(TypeOne, TypeTwo)` to list the input parameters and 114 | `-> ResultType` to list the associated type. But for another, elided 115 | input lifetimes are sugar that introduces higher-ranked bindings. 116 | 117 | For example, these two trait object types are the same: 118 | ```rust 119 | fn identity(bx: Box) -> Box Fn(&'any str)> { 120 | bx 121 | } 122 | ``` 123 | 124 | This is similar to how elided lifetimes work for function declarations 125 | as well, and indeed, the same output lifetime elision rules also apply: 126 | ```rust 127 | // The elided input lifetime becomes a higher-ranked lifetime 128 | // The elided output lifetime is the same as the single input lifetime 129 | // (underneath the binder) 130 | fn identity(bx: Box &str>) -> Box Fn(&'any str) -> &'any str> { 131 | bx 132 | } 133 | ``` 134 | ```rust,compile_fail 135 | // Doesn't compile as what the output lifetime should be is 136 | // considered ambiguous 137 | fn ambiguous(bx: Box &str>) {} 138 | 139 | // Here's a possible fix, which is also an example of 140 | // multiple lifetimes in the binder 141 | fn first(bx: Box Fn(&'a str, &'b str) -> &'a str>) {} 142 | ``` 143 | 144 | Function pointers are another example of types which can be higher-ranked 145 | in Rust. They have analogous syntax and sugar to function declarations 146 | and the `Fn` traits. 147 | ```rust 148 | fn identity(fp: fn(&str) -> &str) -> for<'any> fn(&'any str) -> &'any str { 149 | fp 150 | } 151 | ``` 152 | 153 | ### Syntactic inconsistencies 154 | 155 | There are some inconsistencies around the syntax for function declarations, 156 | function pointer types, and the `Fn` traits involving the "names" of the 157 | input arguments. 158 | 159 | First of all, only function (method) declarations can make use of the 160 | shorthand `self` syntaxes for receivers, like `&self`: 161 | ```rust 162 | # struct S; 163 | impl S { 164 | fn foo(&self) {} 165 | // ^^^^^ 166 | } 167 | ``` 168 | This exception is pretty unsurprising as the `Self` alias only exists 169 | within those implementation blocks. 170 | 171 | Each non-`self` argument in a function declaration is an 172 | [irrefutable pattern](https://doc.rust-lang.org/reference/items/functions.html#function-parameters) 173 | followed by a type annotation. It is an error to leave out the pattern; 174 | if you don't use the argument (and thus don't need to name it), you 175 | still need to use at least the wildcard pattern. 176 | ```rust,compile_fail 177 | fn this_works(_: i32) {} 178 | fn this_fails(i32) {} 179 | ``` 180 | There is 181 | [an accidental exception](https://rust-lang.github.io/rfcs/1685-deprecate-anonymous-parameters.html) 182 | to this rule, but it was removed in Edition 2018 and thus is only 183 | available on Edition 2015. 184 | 185 | In contrast, each argument in a function pointer can be 186 | - An *identifier* followed by a type annotation (`i: i32`) 187 | - `_` followed by a type annotation (`_: i32`) 188 | - Just a type name (`i32`) 189 | 190 | So these all work: 191 | ```rust 192 | let _: fn(i32) = |_| {}; 193 | let _: fn(i: i32) = |_| {}; 194 | let _: fn(_: i32) = |_| {}; 195 | ``` 196 | But *actual* patterns [are not allowed:](https://doc.rust-lang.org/stable/error_codes/E0561.html) 197 | ```rust,compile_fail 198 | let _: fn(&i: &i32) = |_| {}; 199 | ``` 200 | The idiomatic form is to just use the type name. 201 | 202 | It's also allowed [to have colliding names in function pointer 203 | arguments,](https://github.com/rust-lang/rust/issues/33995) but this 204 | is a property of having no function body -- so it's also possible in 205 | a trait method declaration, for example. It is also related to the 206 | Edition 2015 exception for anonymous function arguments mentioned 207 | above, and may be deprecated eventually. 208 | ```rust 209 | trait Trait { 210 | fn silly(a: u32, a: i32); 211 | } 212 | 213 | let _: fn(a: u32, a: i32) = |_, _| {}; 214 | ``` 215 | 216 | Finally, each argument in the `Fn` traits can *only* be a type name: 217 | no identifiers, `_`, or patterns allowed. 218 | ```rust,compile_fail 219 | // None of these compile 220 | let _: Box = Box::new(|_| {}); 221 | let _: Box = Box::new(|_| {}); 222 | let _: Box = Box::new(|_| {}); 223 | ``` 224 | 225 | Why the differences? One reason is that 226 | [patterns are grammatically incompatible with anonymous arguments, 227 | apparently.](https://github.com/rust-lang/rust/issues/41686#issuecomment-366611096) 228 | I'm uncertain as to why identifiers are accepted on function pointers, 229 | however, or more generally why the `Fn` sugar is inconsistent with 230 | function pointer types. But the simplest explanation is that function 231 | pointers existed first with nameable parameters for whatever reason, 232 | whereas the `Fn` sugar is for trait input type parameters which also 233 | do not have names. 234 | 235 | ## Higher-ranked trait bounds 236 | 237 | You can also apply higher-ranked trait bounds (HRTBs) to generic 238 | type parameters, using the same syntax: 239 | ```rust 240 | # trait Look<'s> { fn method(&self, s: &'s str); } 241 | fn box_it_up<'t, T>(t: T) -> Box Look<'any> + 't> 242 | where 243 | T: for<'any> Look<'any> + 't, 244 | { 245 | Box::new(t) 246 | } 247 | ``` 248 | 249 | The sugar for `Fn` like traits applies here as well. You've probably 250 | already seen bounds like this on methods that take closures: 251 | ```rust 252 | # struct S; 253 | # impl S { 254 | fn map<'s, F, R>(&'s self, mut f: F) -> impl Iterator + 's 255 | where 256 | F: FnMut(&[i32]) -> R + 's 257 | { 258 | // This part isn't the point ;-) 259 | [].into_iter().map(f) 260 | } 261 | # } 262 | ``` 263 | 264 | That bound is actually `F: for<'x> FnMut(&'x [i32]) -> R + 's`. 265 | 266 | ## That's all about higher-ranked types for now 267 | 268 | Hopefully this has given you a decent overview of higher-ranked 269 | types, HRTBs, and how they relate to the `Fn` traits. There 270 | are a lot more details and nuances to those topics and related 271 | concepts such as closures, as you might imagine. However, an 272 | exploration of those topics deserves its own dedicated guide, so 273 | we won't see too much more about higher-ranked types in this 274 | tour of `dyn Trait`. 275 | -------------------------------------------------------------------------------- /src/dyn-trait-overview.md: -------------------------------------------------------------------------------- 1 | # `dyn Trait` Overview 2 | 3 | ## What is `dyn Trait`? 4 | 5 | `dyn Trait` is a compiler-provided type which implements `Trait`. Any `Sized` implementor 6 | of `Trait` can be coerced to be a `dyn Trait`, erasing the original base type in the process. 7 | Different implementations of `Trait` may have different sizes, and as a result, `dyn Trait` 8 | has no statically known size. That means it does not implement `Sized`, and we call such 9 | types "unsized", or "dynamically sized types (DSTs)". 10 | 11 | Every `dyn Trait` value is the result of type erasing some other existing value. 12 | You cannot create a `dyn Trait` from a trait definition alone; there must be an 13 | implementing base type that you can coerce. 14 | 15 | Rust currently does not support passing unsized parameters, returning unsized values, or 16 | having unsized locals. Therefore, when interacting with `dyn Trait`, you will generally 17 | be working with some sort of indirection: a `Box`, `&dyn Trait`, `Arc`, 18 | etc. 19 | 20 | And in fact, the indirection is necessary for another reason. These indirections are or 21 | contain *wide pointers* to the erased type, which consist of a pointer to the value, and 22 | a second pointer to a static *vtable*. The vtable in turn contains data such as the size 23 | of the value, a pointer to the value's destructor, pointers to methods of the `Trait`, 24 | and so on. The vtable [enables dynamic dispatch,](./dyn-trait-impls.md) by which 25 | different `dyn Trait` values can dispatch method calls to the different erased base type 26 | implementations of `Trait`. 27 | 28 | `dyn Trait` is also called a "trait object". 29 | 30 | You can also have objects such as `dyn Trait + Send + Sync`. `Send` and `Sync` are 31 | [auto-traits,](https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits) 32 | and a trait object can include any number of these auto traits as additional bounds. 33 | Every distinct set of `Trait + AutoTraits` is a distinct type. 34 | 35 | However, you can only have one *non*-auto trait in a trait object, so this will not 36 | work: 37 | ```rust,compile_fail 38 | trait Trait1 {} 39 | trait Trait2 {}; 40 | struct S(Box); 41 | ``` 42 | 43 | That being noted, [one can usually use a subtrait/supertrait pattern](./dyn-trait-combining.md) 44 | to work around this restriction. 45 | 46 | ### The trait object lifetime 47 | 48 | Confession: we were being imprecise when we said `dyn Trait` is a type. `dyn Trait` is a 49 | *type constructor*: [it is parameterized with a lifetime,](./dyn-trait-lifetime.md) 50 | similar to how references are. So `dyn Trait` on it's own isn't a type, 51 | `dyn Trait + 'a` for some concrete lifetime `'a` is a type. 52 | 53 | The lifetime can usually be elided, [which we will explore later.](./dyn-elision.md) 54 | But it is always part of the type, 55 | [just like a lifetime is part of every reference type,](./st-types.md) 56 | even when elided. 57 | 58 | ### Associated Types 59 | 60 | If a trait has non-generic associated types, those associated types usually become 61 | named parameters of `dyn Trait`: 62 | ```rust 63 | let _: Box> = Box::new([1, 2, 3].into_iter()); 64 | ``` 65 | We explore associated types in `dyn Trait` more 66 | [in a later section.](./dyn-trait-coercions.md#associated-types) 67 | 68 | ## What `dyn Trait` is *not* 69 | 70 | ### `dyn Trait` is not `Sized` 71 | 72 | We mentioned the fact that `dyn Trait` is not `Sized` already. 73 | 74 | However, let us take a moment to note that generic parameters 75 | [have an implicit `Sized` bound.](https://doc.rust-lang.org/reference/special-types-and-traits.html#sized) 76 | Therefore you may need to remove the implicit bound by using 77 | `: ?Sized` in order to use `dyn Trait` in generic contexts. 78 | ```rust,compile_fail 79 | # trait Trait {} 80 | // This function only takes `T: Sized`. It cannot accept a 81 | // `&dyn Trait`, for example, as `dyn Trait` is not `Sized`. 82 | fn foo(_: &T) {} 83 | 84 | // This function takes any `T: Trait`, even if `T` is not 85 | // `Sized`. 86 | fn bar(t: &T) { 87 | // Demonstration that `foo` cannot accept non-`Sized` 88 | // types: 89 | foo(t); 90 | } 91 | ``` 92 | 93 | ### `dyn Trait` is neither a generic nor dynamically typed 94 | 95 | Given a concrete lifetime `'a`, `dyn Trait + 'a` is a statically known type. 96 | The *erased base type* is not statically known, but don't let this confuse 97 | you: the `dyn Trait` itself is its own distinct type and that type is known 98 | at compile time. 99 | 100 | For example, consider these two function signatures: 101 | ```rust 102 | # trait Trait {} 103 | fn generic(_rt: &T) {} 104 | fn not_generic(_dt: &dyn Trait) {} 105 | ``` 106 | 107 | In the generic case, a distinct version of the function will exist for every 108 | type `T` which is passed to the function. This compile-time generation of 109 | new functions for every type is known as monomorphization. (Side note, 110 | lifetimes are erased during compilation, and not monomorphized.) 111 | 112 | You can even create function pointers to the different versions like so: 113 | ```rust 114 | # trait Trait {} 115 | # impl Trait for String {} 116 | # fn generic(_rt: &T) {} 117 | # fn main() { 118 | let fp = generic::; 119 | # } 120 | ``` 121 | That is, the function item type is parameterized by some `T: Trait`. 122 | 123 | In contrast, there will always only be only one `non_generic` function in 124 | the resulting library. The base implementors of `Trait` must be typed-erased 125 | into `dyn Trait + '_` before being passed to the function. The function type 126 | is not parameterized by a generic type. 127 | 128 | Similarly, here: 129 | ```rust 130 | # trait Trait {} 131 | fn generic(bx: Box) {} 132 | ``` 133 | `bx: Box` is not a `Box`. It is a thin owning pointer to a 134 | heap allocated `T` specifically. Because `T` has an implicit `Sized` bound 135 | here, we could *coerce* `bx` to a `Box`. But that would be a 136 | transformation to a different type of `Box`: a wide owning pointer which has 137 | erased `T` and included the corresponding vtable pointer. 138 | 139 | We'll explore more details on the interaction of generics and `dyn Trait` 140 | [in a later section.](./dyn-trait-vs.md) 141 | 142 | You may wonder why you can use the methods of `Trait` on a `&dyn Trait` or 143 | `Box`, etc., despite not declaring any such bound. The reason is 144 | analogous to why you can use `Display` methods on a `String` without declaring 145 | that bound, say: the type is statically known, and the compiler recognizes that 146 | `dyn Trait` implements `Trait`, just like it recognizes that `String` 147 | implements `Display`. Trait bounds are needed for generics, not concrete types. 148 | 149 | (In fact, [`Box` doesn't implement `Trait` automatically,](./dyn-trait-impls.md#boxdyn-trait-and-dyn-trait-do-not-automatically-implement-trait) 150 | but deref coercion usually takes care of that case. For many `std` traits, 151 | the trait is explicitly implemented for `Box` as well; 152 | [we'll also explore what that can look like.](./dyn-trait-box-impl.md)) 153 | 154 | As a concrete type, you can also implement methods on `dyn Trait` 155 | (provided `Trait` is local to your crate), and even implement *other* 156 | traits for `dyn Trait` 157 | (as we will see in [some of the examples](./dyn-trait-examples.md)). 158 | 159 | ### `dyn Trait` is not a supertype 160 | 161 | Because you can coerce base types into a `dyn Trait`, it is not uncommon for 162 | people to think that `dyn Trait` is some sort of supertype over all the 163 | coercible implementors of `Trait`. The confusion is likely exacerbated by 164 | trait bounds and lifetime bounds sharing the same syntax. 165 | 166 | But the coercion from a base type to a `dyn Trait` is an unsizing coercion, 167 | and not a sub-to-supertype conversion; the coercion happens at statically 168 | known locations in your code, and may change the layout of the types 169 | involved (e.g. changing a thin pointer into a wide pointer) as well. 170 | 171 | Relatedly, `trait Trait` is not a class. You cannot create a `dyn Trait` 172 | without an implementing type (they do not have built-in constructors), 173 | and a given type can implement a great many traits. Due to the confusion it 174 | can cause, I recommend not referring to base types as "instances" of the trait. 175 | It is just a type that implements `Trait`, which exists independently of the 176 | trait. When I create a `String`, I'm creating a `String`, not "an instance 177 | of `Display` (and `Debug` and `Write` and `ToString` and ...)". 178 | 179 | When I read "an instance of `Trait`", I assume the variable in question is 180 | some form of `dyn Trait`, and not some unerased base type that implements `Trait`. 181 | 182 | Implementing something for `dyn Trait` does not implement it for all other 183 | `T: Trait`. In fact it implements it for nothing but `dyn Trait` itself. 184 | Implementing something for `dyn Trait + Send` doesn't implement anything 185 | for `dyn Trait` or vice-versa either; those are also separate, distinct types. 186 | 187 | There are ways to *emulate* dynamic typing in Rust, [which we will explore later.](./dyn-any.md) 188 | We'll also explore the role of [*supertraits*](./dyn-trait-combining.md) (which, despite the 189 | name, still do not define a sub/supertype relationship). 190 | 191 | The *only* subtypes in Rust involve lifetimes and types which are 192 | higher-ranked over lifetimes. 193 | 194 | (Pedantic self-correction: trait objects have lifetimes and thus [are supertypes in that sense.](./dyn-covariance.md) However that's not the same concept that most Rust learners get confused about; there is no supertype relationship with the implementing types.) 195 | 196 | ### `dyn Trait` is not universally applicable 197 | 198 | We'll look at the details in their own sections, but in short, you cannot 199 | always coerce an implementor of `Trait` into `dyn Trait`. Both 200 | [the trait](./dyn-safety.md) and [the implementor](dyn-trait-coercions.md) 201 | must meet certain conditions. 202 | 203 | ## In summary 204 | 205 | `dyn Trait + 'a` is 206 | * a concrete, statically known type 207 | * created by type erasing implementors of `Trait` 208 | * used behind wide pointers to the type-erased value and to a static vtable 209 | * dynamically sized (unsized, does not implement `Sized`) 210 | * an implementor of `Trait` via dynamic dispatch 211 | * *not* a supertype of all implementors 212 | * *not* dynamically typed 213 | * *not* a generic 214 | * *not* creatable from all values 215 | * *not* available for all traits 216 | 217 | -------------------------------------------------------------------------------- /src/dyn-safety.md: -------------------------------------------------------------------------------- 1 | # `dyn` safety (object safety) 2 | 3 | There exists traits for which you cannot create a `dyn Trait`: 4 | ```rust,compile_fail 5 | let s = String::new(); 6 | let d: &dyn Clone = &s; 7 | ``` 8 | 9 | Instead of repeating all the rules here, 10 | [I'll just link to the reference.](https://doc.rust-lang.org/reference/items/traits.html#object-safety) 11 | You should go read that first. 12 | 13 | Note that as of this writing, the reference hasn't been updated to document that you 14 | can opt to make associated types and GATs 15 | unavailable to trait objects by adding a `where Self: Sized` bound. For now I'll 16 | refer to this as opting the GAT (or associated type) out of being "`dyn`-usable". 17 | 18 | What may not be immediately apparent is *why* these limitations exists. 19 | The rest of this page explores some of the reasons. 20 | 21 | ## The `Sized` constraints 22 | 23 | Before we get into the restrictions, let's have an aside about how the 24 | `Sized` constraints work with `dyn Trait` and `dyn` safety. 25 | 26 | Rust uses `Sized` to indicate that 27 | - A trait is not `dyn` safe 28 | - An associated type or GAT is not `dyn`-usable 29 | - A method is not `dyn`-dispatchable 30 | - An associated function is not callable for `dyn Trait` 31 | - Even though it never can be (so far), you have to declare this for the sake of being explicit and for potential forwards compatibility 32 | 33 | This makes some sense, as `dyn Trait` is not `Sized`. So a `dyn Trait` 34 | cannot implement a trait with `Sized` as a supertrait, and a `dyn Trait` 35 | can't call methods (or associated functions) that require `Sized` either. 36 | 37 | However, it's still a hack as there are types which are not `Sized` but also 38 | not `dyn Trait`, and we might want to implement our trait for those, *including* 39 | some methods which are not `dyn`-dispatchable (such as generic methods). 40 | Currently that's just not possible in Rust (the non-`dyn`-dispatchable methods 41 | will also not be available for other unsized types). 42 | 43 | The next few paragraphs demonstrate (or perhaps rant about) how this can be an annoying limitation. 44 | If you'd rather get on with learning practical Rust, [you may want to skip ahead 🙂.](#receiver-limitations) 45 | 46 | Consider this example, where we've added a `Sized` bound in order to remain a `dyn`-safe trait: 47 | ```rust 48 | # trait Bound {} 49 | trait Trait { 50 | // Opt-out of `dyn`-dispatchability for this method because it's generic 51 | fn method>(&self) where Self: Sized; 52 | } 53 | ``` 54 | 55 | If you try to implement this trait for `str`, you won't have `method` 56 | available, even if it would logically make sense to have it available. 57 | Moreover, if you write the implementation like so: 58 | ```rust,compile_fail 59 | # trait Bound {} 60 | # trait Trait { 61 | # fn method>(&self) where Self: Sized; 62 | # } 63 | impl Trait for str { 64 | // `Self: Sized` isn't true, so don't bother with `method` 65 | } 66 | ``` 67 | You get an error saying you must provide `method`, even though the 68 | bounds cannot be satisfied. So then you can provide a perfectly 69 | functional implementation: 70 | ```rust,compile_fail 71 | # trait Bound {} 72 | # trait Trait { 73 | # fn method>(&self) where Self: Sized; 74 | # } 75 | impl Trait for str { 76 | fn method>(&self) where Self: Sized { 77 | // do logical `method` things 78 | } 79 | } 80 | ``` 81 | Whoops, it doesn't accept that either! 😠 We have to implement it 82 | without the bound, like so: 83 | ```rust 84 | # trait Bound {} 85 | # trait Trait { 86 | # fn method>(&self) where Self: Sized; 87 | # } 88 | impl Trait for str { 89 | fn method>(&self) { 90 | // do logical `method` things 91 | } 92 | } 93 | ``` 94 | And that compiles... but we can never actually call it. 95 | ```rust,compile_fail 96 | # trait Bound {} 97 | # trait Trait { 98 | # fn method>(&self) where Self: Sized; 99 | # } 100 | # impl Trait for str { 101 | # fn method>(&self) { 102 | # } 103 | # } 104 | fn main() { 105 | "".method(); 106 | } 107 | ``` 108 | Alternatively, we can exploit the fact that higher-ranked bounds 109 | are checked at the call site and not the definition site to sneak 110 | in the unsatisfiable `Self: Sized` bound in a way that compiles: 111 | ```rust 112 | # trait Bound {} 113 | # trait Trait { 114 | # fn method>(&self) where Self: Sized; 115 | # } 116 | impl Trait for str { 117 | // Still not callable, but compiles: vvvvvvv due to this binder 118 | fn method>(&self) where for<'a> Self: Sized { 119 | unreachable!() 120 | } 121 | } 122 | ``` 123 | But naturally the method still cannot be called, as the bound is not satisfiable. 124 | 125 | This is a pretty sad state of affairs. Ideally, there would be a 126 | distinct trait for opting out of `dyn` safety and dispatchability 127 | instead of using `Sized` for this purpose; let's call it `NotDyn`. 128 | Then we could have `Sized: NotDyn` for backwards compatibility, 129 | change the bound above to be `NotDyn`, and have our implementation 130 | for `str` be functional. 131 | 132 | There also some other future possibilities that may improve the situation: 133 | - [Some resolution of RFC issue 2829](https://github.com/rust-lang/rfcs/issues/2829) 134 | or the duplicates linked within would allow omitting the method altogether 135 | (but it would still not be callable) 136 | - [RFC 2056](https://rust-lang.github.io/rfcs/2056-allow-trivial-where-clause-constraints.html) 137 | will allow defining the method with the trivially unsatifiable bound without 138 | exploiting the higher-ranked trick (but it will still not be callable) 139 | - [RFC 3245](https://rust-lang.github.io/rfcs/3245-refined-impls.html) will allow 140 | calling `::method` and refined implementations more generally 141 | 142 | But I feel removing the conflation between `dyn` safety and `Sized` would 143 | be more clear and correct regardless of any future workarounds that may exist. 144 | 145 | ## Receiver limitations 146 | 147 | The requirement for some sort of `Self`-based receiver on `dyn`-dispatchable 148 | methods is to ensure the vtable is available. Some wide pointer to `Self` 149 | needs to be present in order to 150 | [find the vtable and perform dynamic dispatch.](dyn-trait-impls.md#how-dyn-trait-implements-trait) 151 | 152 | Arguably this could be expanded to methods that take a single, 153 | non-receiver `&Self` and so on. 154 | 155 | As for the other limitation on receiver types, [the compiler has to know 156 | how to go backwards from type erased version to original 157 | version](./dyn-trait-impls.md#other-receivers) in order to 158 | implement `Trait`. This may be generalized some day, but for 159 | now it's a restricted set. 160 | 161 | ## Generic method limitations 162 | 163 | In order to support type-generic methods, there would need to be 164 | a function pointer in the vtable for every possible type that the 165 | generic could take on. Not only would this create vtables of 166 | unwieldly size, it would also require some sort of global analysis. 167 | After all, every crate which uses your trait might define new types 168 | that meet the trait bounds in question, and they (or you) might also 169 | want to call the method using those types. 170 | 171 | You can sometimes work around this limitation by type erasing the 172 | generic type parameter in question (in the main method, as an 173 | alternative method, or in a different "erased" trait). 174 | [We'll see an example of this later.](./dyn-trait-erased.md) 175 | 176 | ## Use of `Self` limitations 177 | 178 | Methods which take some form of `Self` other than as a receiver 179 | can depend on the parameter being exactly the same as the 180 | implementing type. But this can't be relied upon once the base 181 | types have been erased. 182 | 183 | For example, consider [`PartialEq`:](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) 184 | ```rust 185 | // Simplified 186 | pub trait PartialEq { 187 | fn partial_eq(&self, rhs: &Self); 188 | } 189 | ``` 190 | If this were implemented for `dyn PartialEq`, the `rhs` parameter 191 | would be a `&dyn PartialEq` like `self` is. But there is no 192 | guarantee that the base types are the same! Both `u8` and `String` 193 | implement `PartialEq` for example, but there's no facility to 194 | compare them for equality (and Rust has no interest in handling 195 | this in an arbitrary way). 196 | 197 | You can sometimes work around this by supplying your own implementations 198 | for some *other* `dyn Trait`, perhaps utilizing the `Any` trait 199 | to emulate dynamic typing and reflection. 200 | [We give an example of this approach later.](./dyn-trait-eq.md) 201 | 202 | [The `impl Clone for Box` example](./dyn-trait-clone.md) 203 | demonstrates handling a case where `Self` is the return value. 204 | 205 | ## GAT limitations 206 | 207 | GATs are too new to support type erasing as-of-yet. We'll need 208 | some way to embed the GAT into the `dyn Trait` as a parameter, 209 | [similar to how is done for non-generic associated types.](./dyn-trait-coercions.md#associated-types) 210 | 211 | [As of Rust 1.72,](https://github.com/rust-lang/rust/pull/112319/) 212 | you can opt out of GATs being `dyn`-usable, and thus out of the 213 | necessity of naming the GAT as a parameter, by adding a 214 | `Self: Sized` bound. 215 | 216 | This is similar to [the same ability on non-generic associated types.](dyn-trait-coercions.md#opting-out-of-dyn-usability) 217 | Interestingly, it allows specifying not only *specific* GAT equalities... 218 | ```rust 219 | trait Trait { 220 | type Gat<'a> where Self: Sized; 221 | } 222 | 223 | impl Trait for () { 224 | type Gat<'a> = &'a str; 225 | } 226 | 227 | let _: &dyn Trait = &'static str> = &(); 228 | ``` 229 | ...but also higher-ranked GAT equality: 230 | ```rust 231 | #trait Trait { 232 | # type Gat<'a> where Self: Sized; 233 | #} 234 | #impl Trait for () { 235 | # type Gat<'a> = &'a str; 236 | #} 237 | // This syntax is still not supported 238 | // let _: &dyn Trait Gat<'a> = &'a str> = &(); 239 | 240 | // However, with `dyn Trait`, you can move the binder to outside the `Trait`: 241 | let _: &dyn for<'a> Trait = &'a str> = &(); 242 | ``` 243 | However, as with the non-generic associated type case, making any use of the 244 | equality would have to be done indirectly, as the `dyn Trait` itself cannot 245 | define a GAT in its own implementation. 246 | 247 | ## Associated constant limitations 248 | 249 | Similarly, supporting associated constants will require at least 250 | [support for associated constant equality.](https://github.com/rust-lang/rust/issues/92827) 251 | 252 | ## Return position `impl Trait` limitations 253 | 254 | Trait methods utilizing 255 | [RPITs](dyn-trait-vs.md#return-position-impl-trait-and-tait) 256 | are, notionally at least, sugar for declaring an opaque associated 257 | type or generic associated type. Additionally, even if the RPIT 258 | captures no generic parameters and thus corresponds to returning 259 | an associated type, there is currently no way to name that associated 260 | type. 261 | 262 | Similar to [generic methods,](#generic-method-limitations) you can 263 | sometimes work around this limitation by type erasing the return 264 | type. (Note that there are some trade-offs, but a discussion of 265 | such is more suited to a dedicated guide about RPITs.) 266 | 267 | ## History 268 | 269 | [Object safety was introduced in RFC 0255,](https://rust-lang.github.io/rfcs/0255-object-safety.html) 270 | and [RFC 0546](https://rust-lang.github.io/rfcs/0546-Self-not-sized-by-default.html) removed the 271 | implied `Sized` bound on traits and added the rule that traits with (explicit) `Sized` bounds 272 | are not object safe. 273 | 274 | Both RFCs were implemented before Rust 1.0. 275 | -------------------------------------------------------------------------------- /src/dyn-trait-impls.md: -------------------------------------------------------------------------------- 1 | # `dyn Trait` implementations 2 | 3 | In order for `dyn Trait` to be useful for abstracting over the base 4 | types which implement `Trait`, `dyn Trait` itself needs to implement 5 | `Trait`. The compiler always supplies that implementation. Here we 6 | look at how this notionally works, and also touch on how this leads 7 | to some related limitations around `dyn Trait`. 8 | 9 | We also cover a few surprising corner-cases related to how the 10 | implementation of `Trait` for `dyn Trait` works... or doesn't. 11 | 12 | ## How `dyn Trait` implements `Trait` 13 | 14 | Let us note upfront: this is a rough sketch, and not normative. What the 15 | compiler actually does is an implementation detail. But by providing a 16 | sketch of how it *could* be implemented, we hope to provide some intuition 17 | for `dyn Trait` being a concrete type, and some explanation of the 18 | limitations that `dyn Trait` has. 19 | 20 | With that disclaimer out of the way, let's look at what the compiler 21 | implementation might look like for this trait: 22 | ```rust 23 | trait Trait { 24 | fn look(&self); 25 | fn add(&mut self, s: String) -> i32; 26 | } 27 | ``` 28 | 29 | Recall that when dealing with `dyn Trait`, you'll be dealing with 30 | a pointer to the erased base type, and with a vtable. For example, 31 | we could imagine a `&dyn Trait` looks something like this: 32 | ```rust,ignore 33 | #[repr(C)] 34 | struct DynTraitRef<'a> { 35 | _lifetime: PhantomData<&'a ()>, 36 | base_type: *const (), 37 | vtable: &'static DynTraitVtable, 38 | } 39 | 40 | // Pseudo-code 41 | type &'a dyn Trait = DynTraitRef<'a>; 42 | ``` 43 | Here we're using a thin `*const ()` to point to the erased base type. 44 | Similarly, you can imagine a `DynTraitMut<'a>` for `&'a mut dyn Trait` 45 | that uses `*mut ()`. 46 | 47 | And the vtable might look something like this: 48 | ```rust 49 | #[repr(C)] 50 | struct DynTraitVtable { 51 | fn_drop: fn(*mut ()), 52 | type_size: usize, 53 | type_alignment: usize, 54 | fn_look: fn(*const ()), 55 | fn_add: fn(*mut (), s: String) -> i32, 56 | } 57 | ``` 58 | 59 | And the implementation itself could look something like this: 60 | ```rust,ignore 61 | impl Trait for dyn Trait + '_ { 62 | fn look(&self) { 63 | (self.vtable.fn_look)(self.base_type) 64 | } 65 | fn add(&mut self, s: String) -> i32 { 66 | (self.vtable.fn_add)(self.base_type, s) 67 | } 68 | } 69 | ``` 70 | 71 | In summary, we've erased the base type by replacing references to the 72 | base type with the appropriate type of pointer to the same data, both 73 | in the wide references (`&dyn Trait`, `&mut dyn Trait`), and also in 74 | the vtable function pointers. The compiler guarantees there's no ABI 75 | mismatch. 76 | 77 | *Reminder:* This is just a rough sketch on how `dyn Trait` can be 78 | implemented to aid the high-level understanding and discussion, and 79 | not necessary exactly how they *are* implemented. 80 | 81 | [Here's another blog post on the topic.](https://huonw.github.io/blog/2015/01/peeking-inside-trait-objects/) 82 | Note that it was written in 2015, and some things in Rust have changed 83 | since that time. For example, [trait objects used to be "spelled" just 84 | `Trait` instead of `dyn Trait`.](https://rust-lang.github.io/rfcs/2113-dyn-trait-syntax.html) 85 | You'll have to figure out if they're talking about the trait or the 86 | `dyn Trait` type from context. 87 | 88 | ## Other receivers 89 | 90 | Let's look at one other function signature: 91 | ```rust 92 | trait Trait { 93 | fn eat_box(self: Box); 94 | } 95 | ``` 96 | 97 | How does this work? Internally, a `Box` is 98 | a thin pointer, while a `Box` is wide pointer, very similar 99 | to `&mut dyn Trait` for example (although the `Box` pointer implies ownership and 100 | not just exclusivity). The implementation for this method would be 101 | similar to that of `&mut dyn Trait` as well: 102 | ```rust,ignore 103 | // Still just for illustrative purpose 104 | impl Trait for dyn Trait + '_ { 105 | fn eat_box(self: Box) { 106 | let BoxRepresentation { base_type, vtable } = self; 107 | let boxed_type = Box::from_raw(base_type); 108 | (vtable.fn_eat_box)(boxed_type); 109 | } 110 | } 111 | ``` 112 | 113 | In short, the compiler knows how to go from the type-erased form 114 | (like `Box`) into something ABI compatible for the base type 115 | (`Box`) for every supported receiver type. 116 | 117 | It's an implementation detail, but currently the way the compiler 118 | knows how to do the conversion is via the 119 | [`DispatchFromDyn`](https://doc.rust-lang.org/std/ops/trait.DispatchFromDyn.html) 120 | trait. The documentation lists the current limitations of supported 121 | types (some of which are only available under the unstable 122 | [`arbitrary_self_types` feature](https://github.com/rust-lang/rust/issues/44874)). 123 | 124 | ## Supertraits are also implemented 125 | 126 | [We'll look at supertraits in more detail later,](./dyn-trait-combining.md) but 127 | here we'll briefly note that when you have a supertrait: 128 | ``` 129 | trait SuperTrait { /* ... */ } 130 | trait Trait: SuperTrait { /* ... */ } 131 | ``` 132 | The vtable for `dyn Trait` includes the methods of `SuperTrait` and the compiler 133 | supplies an implementation of `SuperTrait` for `dyn Trait`, just as it supplies 134 | an implementation of `Trait`. 135 | 136 | ## `Box` and `&dyn Trait` do not automatically implement `Trait` 137 | 138 | It may come as a surprise that neither `Box` nor 139 | `&dyn Trait` automatically implement `Trait`. Why not? 140 | 141 | In short, because it's not always possible. 142 | 143 | [As we'll cover later,](dyn-safety.md#the-sized-constraints) a trait 144 | may have methods which are not dispatchable by `dyn Trait`, but must 145 | be implemented for any `Sized` type. One example is associated 146 | functions that have no receiver: 147 | ```rust 148 | trait Trait { 149 | fn no_receiver() -> String where Self: Sized; 150 | } 151 | ``` 152 | 153 | There's no way for the compiler to generate the body of such an associated 154 | function, and it can't provide a complete `Trait` implementation without 155 | one. 156 | 157 | Additionally, the receivers of dispatchable methods don't always make 158 | sense: 159 | ```rust 160 | trait Trait { 161 | fn takes_mut(&mut self); 162 | } 163 | ``` 164 | 165 | A `&dyn Trait` can produce a `&BaseType`, but not a `&mut BaseType`, so 166 | there is no way to implement `Trait::takes_mut` for `&dyn Trait` when 167 | the only pre-existing implementation is for `BaseType`. 168 | 169 | Similarly, an `Arc` has no way to call a `Box` 170 | or vice-versa, and so on. 171 | 172 | ### Implementing these yourself 173 | 174 | If `Trait` is a local trait, you can implement it for `Box` 175 | and so on just like you would for any other type. Take care though, as it 176 | can be easy to accidentally write a recursive definition! 177 | 178 | [We walk through an example of this later on.](./dyn-trait-box-impl.md) 179 | 180 | Moreover, `&T`, `&mut T`, and `Box` are 181 | [*fundamental*,](https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html#definitions) 182 | which means that when it comes to the orphan rules (which gate which trait 183 | implementations you can write), they act the same as `T`. Additionally, 184 | if `Trait` is a local trait, then `dyn Trait + '_` is a local type. 185 | 186 | Together that means that *you can even implement **other** traits* for 187 | `Box` (and other fundamental wrappers)! 188 | 189 | [We also have an example of this later on.](dyn-trait-clone.md) 190 | 191 | Unfortunately, `Rc`, `Arc`, and so on are not fundamental, so this doesn't 192 | cover every possible use case. 193 | 194 | ## The implementation cannot be directly overrode 195 | 196 | The compiler provided implementation of `Trait` for `dyn Trait` cannot be 197 | overrode by an implementation in your code. If you attempt to define your 198 | own definition directly, you'll get a compiler error: 199 | ```rust,compile_fail 200 | trait Trait {} 201 | impl Trait for dyn Trait + '_ {} 202 | ``` 203 | 204 | And if you have a blanket implementation to implement `Trait` and `dyn Trait` 205 | happens to meet the bounds on the implementation, it will be ignored and the 206 | compiler defined implementation will still be used: 207 | ```rust 208 | #use std::any::type_name; 209 | # 210 | trait Trait { 211 | fn hi(&self) { 212 | println!("Hi from {}!", type_name::()); 213 | } 214 | } 215 | 216 | // The simplest example is an implementation for absolutely everything 217 | impl Trait for T {} 218 | 219 | let dt: &dyn Trait = &(); 220 | // Prints "Hi from ()!" and not "Hi from dyn Trait!" 221 | dt.hi(); 222 | // Same thing 223 | ::hi(dt); 224 | ``` 225 | 226 | [This even applies with more complicated implementations,](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=66a0de00d42d915134f206ee73291136) 227 | and applies to the supertrait implementations for `dyn Trait` as well. 228 | 229 | [We'll see that this can be useful later.](./dyn-trait-erased.md) But 230 | unfortunately, [there are some compiler bugs around the compiler 231 | implementation taking precedence over your blanket implementations.](https://github.com/rust-lang/rust/issues/57893#issuecomment-510690333) 232 | How those bugs are dealt with is yet to be determined; it's possible 233 | that certain blanket implementations will be disallowed, or that 234 | some traits will no longer be `dyn`-safe. (The *general* pattern, 235 | such as the simple example above, is almost surely too widespread 236 | to be deprecated.) 237 | 238 | ## The implementation cannot be indirectly bypassed 239 | 240 | You may be aware that when a concrete type has an inherent method with 241 | the same name and receiver as a trait method, the inherent method takes 242 | precedence when performing method lookup: 243 | ```rust 244 | trait Trait { fn method(&self) { println!("In trait Trait"); } } 245 | 246 | struct S; 247 | impl Trait for S {} 248 | impl S { fn method(&self) { println!("In impl S"); } } 249 | 250 | fn main() { 251 | let s = S; 252 | s.method(); 253 | // If you wanted to use the trait, you can do this 254 | ::method(&s); 255 | } 256 | ``` 257 | 258 | Unfortunately, this functionality is not available for `dyn Trait`. 259 | You can write the implementation, but unlike the example above, they 260 | will be considered ambiguous with the trait methods: 261 | ```rust,compile_fail 262 | trait Trait { 263 | fn method(&self) {} 264 | fn non_dyn_dispatchable(&self) where Self: Sized {} 265 | } 266 | 267 | impl dyn Trait + '_ { 268 | fn method(&self) {} 269 | fn non_dyn_dispatchable(&self) {} 270 | } 271 | 272 | fn foo(d: &dyn Trait) { 273 | d.method(); 274 | d.non_dyn_dispatchable(); 275 | } 276 | ``` 277 | 278 | Moreover, there is no syntax to call the inherent methods specifically 279 | like there is for normal `struct`s. 280 | [Even if you try to hide the trait,](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=6f55d2ebb8b44349034fc4df120e152f) 281 | the inherent methods are unreachable, dead code. 282 | 283 | Apparently the idea is that the trait methods "are" the inherent methods of 284 | `dyn Trait`, but this is rather unfortunate as it prevents directly providing 285 | something like the `non_dyn_dispatchable` override attempted above. 286 | See [issue 51402](https://github.com/rust-lang/rust/issues/51402) for more 287 | information. 288 | 289 | Implementing methods on `dyn Trait` that don't attempt to shadow the 290 | methods of `Trait` does work, however. 291 | ```rust 292 | #trait Trait {} 293 | impl dyn Trait + '_ { 294 | fn some_other_method(&self) {} 295 | } 296 | 297 | fn bar(d: &dyn Trait) { 298 | d.some_other_method(); 299 | } 300 | ``` 301 | 302 | ## A niche exception to `dyn Trait: Trait` 303 | 304 | Some bounds on traits aren't checked until you try to utilize the trait, 305 | even when the trait is considered object safe. As a result, [it is 306 | actually sometimes possible to create a `dyn Trait` that does not implement 307 | `Trait`!](https://github.com/rust-lang/rust/issues/88904) 308 | 309 | ```rust,compile_fail 310 | trait Iterable 311 | where 312 | for<'a> &'a Self: IntoIterator< 313 | Item = &'a ::Borrow, 314 | >, 315 | { 316 | type Borrow; 317 | fn iter(&self) -> Box + '_> { 318 | Box::new(self.into_iter()) 319 | } 320 | } 321 | 322 | impl Iterable for I 323 | where 324 | for<'a> &'a Self: IntoIterator, 325 | { 326 | type Borrow = Borrow; 327 | } 328 | 329 | fn example(v: Vec) { 330 | // This compiles, demonstrating that we can create `dyn Iterable` 331 | // (i.e. the trait is object safe and `v` can be coerced) 332 | let dt: &dyn Iterable = &v; 333 | 334 | // But this gives an error as `&dyn Iterable` doesn't meet the trait 335 | // bound, and thus `dyn Iterable` does not implement `Iterable`! 336 | for item in dt.iter() { 337 | println!("{item}"); 338 | } 339 | } 340 | ``` 341 | 342 | With this particular example, [it's possible to provide an implementation such that 343 | `dyn Iterable` meets the bounds.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7b897bcb453c6d8b7b8e3461f70db7a6) 344 | If that's not possible, you probably need to drop the bound or give up 345 | on the trait being `dyn`-safe. 346 | -------------------------------------------------------------------------------- /src/dyn-trait-eq.md: -------------------------------------------------------------------------------- 1 | # Downcasting `Self` parameters 2 | 3 | Now let's move on to something a little more complicated. We 4 | [mentioned before](./dyn-safety.md#use-of-self-limitations) that 5 | `Self` is not accepted outside of the receiver, such as when it's 6 | another parameter, as there is no guarantee that the other 7 | parameter has the same base type as the receiver (and if they 8 | are not the same base type, there is no actual implementation to 9 | call). 10 | 11 | Let's see how we can work around this to implement 12 | [`PartialOrd`](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) 13 | for `dyn Trait`, despite the `&Self` parameter. The trait is a good 14 | fit in the face of type erasure, as we can just return `None` when 15 | the types don't match, indicating that comparison is not possible. 16 | 17 | `PartialOrd` requires `PartialEq`, so we'll tackle that as well. 18 | 19 | ## Downcasting with `dyn Any` to emulate dynamic typing 20 | 21 | We haven't had to use `dyn Any` in the previous examples, because 22 | we've been able to maneuver our implementations in such a way that 23 | dynamic dispatch implicitly "downcasted" our erased types to their 24 | concrete base types for us. It's able to do this because the pointer 25 | to the base type is coupled with a vtable that only accepts said base 26 | type, and there is no need for actual dynamic typing or comparing types 27 | at runtime. The conversion is infallible for those cases. 28 | 29 | However, now we have two wide pointers which may point to different 30 | base types. In this particular application, we only really need to 31 | know if they have the same base type or not... though it would be 32 | nice to have some *safe* way to recover the erased type of non-receiver 33 | too, instead of whatever casting shenanigans might be necessary. 34 | 35 | You might think you could somehow use the vtable pointers to see if 36 | the base types are the same. But unfortunately, [we can't rely on the 37 | vtable to compare their types at runtime.](https://doc.rust-lang.org/std/ptr/fn.eq.html) 38 | 39 | > When comparing wide pointers, both the address and the metadata are 40 | tested for equality. However, note that comparing trait object pointers 41 | (`*const dyn Trait`) is unreliable: pointers to values of the same 42 | underlying type can compare unequal (because vtables are duplicated in 43 | multiple codegen units), and pointers to values of different underlying 44 | type can compare equal (since identical vtables can be deduplicated 45 | within a codegen unit). 46 | 47 | That's right, false negatives *and* false positives. Fun! 48 | 49 | So we need a different mechanism to compare types and know when we 50 | have two wide pointers to the same base type, and that's where `dyn Any` 51 | comes in. [`Any`](https://doc.rust-lang.org/std/any/trait.Any.html) is 52 | the trait to emulate dynamic typing, and 53 | [many fallible downcasting methods](https://doc.rust-lang.org/std/any/trait.Any.html#implementations) 54 | are supplied for the type-erased forms of `dyn Any`, `Box`, 55 | et cetera. This will allow us to not just compare for base type equality, 56 | but also to safely recover the erased base type ("downcast"). 57 | 58 | The `Any` trait comes with a `'static` constraint for soundness reasons, 59 | so note that our base types are going to be more limited for this example. 60 | 61 | Additionally, the [lack of supertrait upcasting](dyn-trait-coercions.md#supertrait-upcasting) 62 | is going to make things less ergonomic than they will be once that feature is 63 | available. 64 | 65 | One last side note, [we look at `dyn Any` in a bit more detail later.](./dyn-any.md) 66 | 67 | Well enough meta, let's dive in! 68 | 69 | ## `PartialEq` 70 | 71 | The general idea is that we're going to have a comparison trait, `DynCompare`, 72 | and then implement `PartialEq` for `dyn DynCompare` in a universal manner. 73 | Then our actual trait (`Trait`) can have `DynCompare` as a supertrait, and 74 | implement `PartialEq` for `dyn Trait` by upcasting to `dyn DynCompare`. 75 | 76 | In the implementation for `dyn DynCompare`, we're going to have to (attempt to) 77 | downcast to the erased base type. For that to be available we will need to 78 | first be able to upcast from `dyn DynCompare` to `dyn Any`. 79 | 80 | As the first step, we're going to use the "supertrait we can blanket implement" 81 | pattern yet again to make a trait that can handle all of our supertrait upcasting needs. 82 | 83 | Here it is, [similar to how we've done it before:](dyn-trait-combining.md#manual-supertrait-upcasting) 84 | ```rust 85 | use std::any::Any; 86 | 87 | trait AsDynCompare: Any { 88 | fn as_any(&self) -> &dyn Any; 89 | fn as_dyn_compare(&self) -> &dyn DynCompare; 90 | } 91 | 92 | // Sized types only 93 | impl AsDynCompare for T { 94 | fn as_any(&self) -> &dyn Any { 95 | self 96 | } 97 | fn as_dyn_compare(&self) -> &dyn DynCompare { 98 | self 99 | } 100 | } 101 | 102 | trait DynCompare: AsDynCompare { 103 | fn dyn_eq(&self, other: &dyn DynCompare) -> bool; 104 | } 105 | ``` 106 | There's an `Any: 'static` bound which applies to `dyn Any + '_`, so 107 | [all of those `&dyn Any` are actually `&dyn Any + 'static`.](dyn-elision-trait-bounds.md#the-static-case) 108 | I have also included an `Any` supertrait to `AsDynCompare`, so the 109 | "always `'static`" property holds for `&dyn DynCompare` as well, even 110 | though it isn't strictly necessary. This way, we don't have to worry 111 | about being flexible with the trait object lifetime at all -- it is 112 | just always `'static`. 113 | 114 | The downside is that only base types that satisfy the `'static` bound 115 | can be supported, so there may be niche circumstances where you don't 116 | want to include the supertrait bound. However, given that we need to 117 | upcast to `dyn Any`, this must mean you're pretending to be another 118 | type, which seems quite niche indeed. If you do try the non-`'static` 119 | route for your own use case, note that some of the implementations in 120 | this example could be made more general. 121 | 122 | Anyway, let's move on to performing cross-type equality checking: 123 | ```rust 124 | #use std::any::Any; 125 | # 126 | #trait AsDynCompare: Any { 127 | # fn as_any(&self) -> &dyn Any; 128 | # fn as_dyn_compare(&self) -> &dyn DynCompare; 129 | #} 130 | # 131 | #impl AsDynCompare for T { 132 | # fn as_any(&self) -> &dyn Any { 133 | # self 134 | # } 135 | # fn as_dyn_compare(&self) -> &dyn DynCompare { 136 | # self 137 | # } 138 | #} 139 | # 140 | #trait DynCompare: AsDynCompare { 141 | # fn dyn_eq(&self, other: &dyn DynCompare) -> bool; 142 | #} 143 | impl DynCompare for T { 144 | fn dyn_eq(&self, other: &dyn DynCompare) -> bool { 145 | if let Some(other) = other.as_any().downcast_ref::() { 146 | self == other 147 | } else { 148 | false 149 | } 150 | } 151 | } 152 | 153 | // n.b. this could be implemented in a more general way when 154 | // the trait object lifetime is not constrained to `'static` 155 | impl PartialEq for dyn DynCompare { 156 | fn eq(&self, other: &dyn DynCompare) -> bool { 157 | self.dyn_eq(other) 158 | } 159 | } 160 | ``` 161 | 162 | Here we've utilized our `dyn Any` upcasting to try and recover a 163 | parameter of our own base type, and if successful, do the actual 164 | (partial) comparison. Otherwise we say they're not equal. 165 | 166 | This allows us to implement `PartialEq` for `dyn Compare`. 167 | 168 | Then we want to wire this functionality up to our actual trait: 169 | ```rust 170 | #use std::any::Any; 171 | # 172 | #trait AsDynCompare: Any { 173 | # fn as_any(&self) -> &dyn Any; 174 | # fn as_dyn_compare(&self) -> &dyn DynCompare; 175 | #} 176 | # 177 | #impl AsDynCompare for T { 178 | # fn as_any(&self) -> &dyn Any { 179 | # self 180 | # } 181 | # fn as_dyn_compare(&self) -> &dyn DynCompare { 182 | # self 183 | # } 184 | #} 185 | # 186 | #trait DynCompare: AsDynCompare { 187 | # fn dyn_eq(&self, other: &dyn DynCompare) -> bool; 188 | #} 189 | #impl DynCompare for T { 190 | # fn dyn_eq(&self, other: &dyn DynCompare) -> bool { 191 | # if let Some(other) = other.as_any().downcast_ref::() { 192 | # self == other 193 | # } else { 194 | # false 195 | # } 196 | # } 197 | #} 198 | # 199 | #impl PartialEq for dyn DynCompare { 200 | # fn eq(&self, other: &dyn DynCompare) -> bool { 201 | # self.dyn_eq(other) 202 | # } 203 | #} 204 | trait Trait: DynCompare {} 205 | impl Trait for i32 {} 206 | impl Trait for bool {} 207 | 208 | impl PartialEq for dyn Trait { 209 | fn eq(&self, other: &dyn Trait) -> bool { 210 | self.as_dyn_compare() == other.as_dyn_compare() 211 | } 212 | } 213 | ``` 214 | 215 | The supertrait bound does most of the work, and we just use 216 | upcasting again -- to `dyn DynCompare` this time -- to be 217 | able to perform `PartialEq` on our `dyn Trait`. 218 | 219 | [A blanket implementation in `std`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#impl-PartialEq%3CBox%3CT,+A%3E%3E-for-Box%3CT,+A%3E) 220 | gives us `PartialEq` for `Box` automatically. 221 | 222 | Now let's try it out: 223 | ```rust,compile_fail 224 | #use std::any::Any; 225 | # 226 | #trait AsDynCompare: Any { 227 | # fn as_any(&self) -> &dyn Any; 228 | # fn as_dyn_compare(&self) -> &dyn DynCompare; 229 | #} 230 | # 231 | #impl AsDynCompare for T { 232 | # fn as_any(&self) -> &dyn Any { 233 | # self 234 | # } 235 | # fn as_dyn_compare(&self) -> &dyn DynCompare { 236 | # self 237 | # } 238 | #} 239 | # 240 | #trait DynCompare: AsDynCompare { 241 | # fn dyn_eq(&self, other: &dyn DynCompare) -> bool; 242 | #} 243 | # 244 | #impl DynCompare for T { 245 | # fn dyn_eq(&self, other: &dyn DynCompare) -> bool { 246 | # if let Some(other) = other.as_any().downcast_ref::() { 247 | # self == other 248 | # } else { 249 | # false 250 | # } 251 | # } 252 | #} 253 | # 254 | #impl PartialEq for dyn DynCompare { 255 | # fn eq(&self, other: &dyn DynCompare) -> bool { 256 | # self.dyn_eq(other) 257 | # } 258 | #} 259 | # 260 | #trait Trait: DynCompare {} 261 | #impl Trait for i32 {} 262 | #impl Trait for bool {} 263 | # 264 | #impl PartialEq for dyn Trait { 265 | # fn eq(&self, other: &dyn Trait) -> bool { 266 | # self.as_dyn_compare() == other.as_dyn_compare() 267 | # } 268 | #} 269 | fn main() { 270 | let bx1a: Box = Box::new(1); 271 | let bx1b: Box = Box::new(1); 272 | let bx2: Box = Box::new(2); 273 | let bx3: Box = Box::new(true); 274 | 275 | println!("{}", bx1a == bx1a); 276 | println!("{}", bx1a == bx1b); 277 | println!("{}", bx1a == bx2); 278 | println!("{}", bx1a == bx3); 279 | } 280 | ``` 281 | Uh... it didn't work, but for weird reasons. Why is it trying to move out of the 282 | `Box` for a comparison? As it turns out, this is [a longstanding bug in the 283 | language.](https://github.com/rust-lang/rust/issues/31740) Fortunately that issue 284 | also offers a workaround that's ergonomic at the use site: implement `PartialEq<&Self>` 285 | too. 286 | 287 | ```rust 288 | #use std::any::Any; 289 | # 290 | #trait AsDynCompare: Any { 291 | # fn as_any(&self) -> &dyn Any; 292 | # fn as_dyn_compare(&self) -> &dyn DynCompare; 293 | #} 294 | # 295 | #// Sized types only 296 | #impl AsDynCompare for T { 297 | # fn as_any(&self) -> &dyn Any { 298 | # self 299 | # } 300 | # fn as_dyn_compare(&self) -> &dyn DynCompare { 301 | # self 302 | # } 303 | #} 304 | # 305 | #trait DynCompare: AsDynCompare { 306 | # fn dyn_eq(&self, other: &dyn DynCompare) -> bool; 307 | #} 308 | # 309 | #impl DynCompare for T { 310 | # fn dyn_eq(&self, other: &dyn DynCompare) -> bool { 311 | # if let Some(other) = other.as_any().downcast_ref::() { 312 | # self == other 313 | # } else { 314 | # false 315 | # } 316 | # } 317 | #} 318 | # 319 | #impl PartialEq for dyn DynCompare { 320 | # fn eq(&self, other: &dyn DynCompare) -> bool { 321 | # self.dyn_eq(other) 322 | # } 323 | #} 324 | # 325 | #trait Trait: DynCompare {} 326 | #impl Trait for i32 {} 327 | #impl Trait for bool {} 328 | # 329 | #impl PartialEq for dyn Trait { 330 | # fn eq(&self, other: &dyn Trait) -> bool { 331 | # self.as_dyn_compare() == other.as_dyn_compare() 332 | # } 333 | #} 334 | # 335 | // New 336 | impl PartialEq<&Self> for Box { 337 | fn eq(&self, other: &&Self) -> bool { 338 | ::eq(self, *other) 339 | } 340 | } 341 | 342 | fn main() { 343 | let bx1a: Box = Box::new(1); 344 | let bx1b: Box = Box::new(1); 345 | let bx2: Box = Box::new(2); 346 | let bx3: Box = Box::new(true); 347 | 348 | println!("{}", bx1a == bx1a); 349 | println!("{}", bx1a == bx1b); 350 | println!("{}", bx1a == bx2); 351 | println!("{}", bx1a == bx3); 352 | } 353 | ``` 354 | Ok, now it works. Phew! 355 | 356 | ## `PartialOrd` 357 | 358 | From here it's mostly mechanical to add `PartialOrd` support: 359 | ```diff 360 | +use core::cmp::Ordering; 361 | 362 | trait DynCompare: AsDynCompare { 363 | fn dyn_eq(&self, other: &dyn DynCompare) -> bool; 364 | + fn dyn_partial_cmp(&self, other: &dyn DynCompare) -> Option; 365 | } 366 | 367 | -impl DynCompare for T { 368 | +impl DynCompare for T { 369 | fn dyn_eq(&self, other: &dyn DynCompare) -> bool { 370 | if let Some(other) = other.as_any().downcast_ref::() { 371 | self == other 372 | } else { 373 | false 374 | } 375 | } 376 | + 377 | + fn dyn_partial_cmp(&self, other: &dyn DynCompare) -> Option { 378 | + other 379 | + .as_any() 380 | + .downcast_ref::() 381 | + .and_then(|other| self.partial_cmp(other)) 382 | + } 383 | } 384 | 385 | +impl PartialOrd for dyn DynCompare { 386 | + fn partial_cmp(&self, other: &dyn DynCompare) -> Option { 387 | + self.dyn_partial_cmp(other) 388 | + } 389 | +} 390 | 391 | +impl PartialOrd for dyn Trait { 392 | + fn partial_cmp(&self, other: &dyn Trait) -> Option { 393 | + self.as_dyn_compare().partial_cmp(other.as_dyn_compare()) 394 | + } 395 | +} 396 | 397 | +impl PartialOrd<&Self> for Box { 398 | + fn partial_cmp(&self, other: &&Self) -> Option { 399 | + ::partial_cmp(self, *other) 400 | + } 401 | +} 402 | ``` 403 | 404 | [Here's the final playground.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8142d76abd2b2a4ef75950029e035371) 405 | --------------------------------------------------------------------------------