impl Debug for AnyIterable // How to debug print an `AnyIterable`
where ... { // ... But it's only possible sometimes
// Required method
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { ... }
}
As for the bounds, there are two:
// Any reference to `AnyIterable` must be iterable...
for<'a> &'a Self: IntoIterator,
// ... and the items yielded by that iterator must implement `Debug`
for<'a> <&'a Self as IntoIterator>::Item: Debug
These capabilities are then used within fmt implementation. In particular, they match the requirements imposed by the argument to DebugList::entries().
The for<'a> part is known as a higher-ranked trait bound and is necessary in this case because the reference &self doesn't have a lifetime that can be named in the bounds— It could be anything, and so we need to make sure the code is valid for everything.
Not off the top of my head, but the key thing to remember is that they have a dual purpose:
Seen from the outside, they are the requirements that you need to fulfill in order to use the guarded code.
Seen from the inside, they are the capabilities that you are allowed to use in your implementation.
Combined, these two ensure that any uses of this code that the trait solver accepts will actually result in functional code. This is stricter than something like C++'s template system, with the aim of making the compilation errors easier to understand and reason about (because they should all arise from a violation of an explicit contract instead of an implicit one).
We can also ask, what's the difference between these?
fn foo<T>(t: &T) where for<'a> &'a T: Trait {}
fn bar<'b, U>(u: &'b U) where &'b U: Trait {}
One difference is that bar only talks about a single lifetime ("exists 'b such that..."), which can be important for higher-ranked bounds. But there is another crucial difference.
With bar, the caller chooses the lifetime 'b, and the function body has to work for every possible choice -- like 'static, say. In particular, you can't assume they'll choose a local lifetime,[1] so the function body can never assume the borrow of a local meets the bound. (Therefore it's generally only useful if the caller also supplies a &'b U.)
In contrast the bound on foodoes apply to local borrows. Sometimes this is phrased as applying to arbitrarily short lifetimes (shorter than the caller can name). This can matter when you need things to work for local lifetimes, like when you need to pass a reference with the lifetime due to the trait definition as one example.
In my experience it's more common to have a related scenario, where instead of bounds on references, you have bounds on type parameters involving traits with a lifetime parameter.
(Then things become a bit more complicated when you have implementations of traits with lifetimes for types that also have lifetimes...)
If you're not familiar with mathematics, it might help to think of 'a in for<'a> as roughly being "the worst lifetime you could possibly get". Often, that's the longest lifetime 'static or the ephemeral lifetime (no syntax), but sometimes it's neither.
for<'a> in trait bounds can be especially unintuitive, as extra syntax in a trait bound would usually strengthen it, causing some types to no longer fulfill it. But the semantics of for<'a> doesn't do that. Instead, it expresses a lack of outlive relations.
Normally, named lifetimes in trait impls are linked to a part of the thing you're implementing the trait on.
For example, the trait impl below is saying that a borrow of Self has to meet the trait bound, but only if it lives as long as 'a[1].
impl <'a> Trait for Struct<'a>
where &'a Self: RequiredTrait
{
fn foo(&self) {
// Here, the elided lifetime in &self expands to
// &'shorter_than_a Self, which may as well not
// implement RequiredTrait.
// Therefore, trying to call RequiredTrait's method on
// self would result in a compile error.
}
}
In contrast, for<'a> introduces a "fresh" lifetime, opting out of this linkage to another, already-established lifetime.
Rust references are full-fledged types, so if a reference is the implementing type, then method's receiver (Self) is a reference.
That's why the snippets you wrote which work are possible: the bounds say that the implementing type is a reference, and you have a reference to call the method with. Whereas when (by the bounds) the implementing type was T and you had a &T (and T was not Copy), you couldn't get a T to call the method with. The types didn't line up.
So self here is actually &T. Did I get it correctly?
I have two questions:
In the following code I think t should be moved because method consumes t(because of self), but the code works fine, why?!
And in the following code I get error that '&' without an explicit lifetime name cannot be used here, and here is that we will need using lifetimes explicitly. firstly why do we need explicitly define lifetimes here?
Here t is &T, a shared reference, and shared references are Copy. So t doesn't get consumed. [1]
It helps to remember that &T is not, in fact, a single type but rather a family of types. It's a convenience notation for &'a T for some lifetime 'a. Pertinently, if &T occurs twice in a function signature, the two instances don't necessarily refer to the same &'a T. But in several common cases the compiler allows you to elide the lifetime annotations and infers that you most likely want two instances of T& to have compatible lifetimes. As such, for example, this signature:
fn foo(&T) -> &U
is in fact short for
fn foo<'a>(&'a T) -> &'a U
because typically such a signature is a projection (getter) of some sort, that is, it returns a reference to a field of T, which naturally shares the lifetime of the containing object.
But here
there is no lifetime elision and thus the where clause by default doesn't bound the argument type in any way -- there's no single "overwhelmingly likely" option for what the lifetimes should be, so the compiler requires you to be explicit.
This works if the called method takes &self; if it took self this wouldn't compile because you can't consume (move out) stuff through a shared reference. ↩︎
In this particular example, the bounds say that Trait (which provides method()) is implemented on &T. This allows a self-taking method to be used because the Self type (&T) implements Copy.
Yes, self is &'s T for some particular lifetime 's.
// `&'x T` has to implement `Trait` for every `'x`
fn bar<T>(t: &T) where for<'a> &'a T: Trait,
// `&'x T` has to implement `Trait` for `'a` specifically
fn baz<'a, T>(t: &'a T) where &'a T: Trait {
Because all shared references (&T) implement Copy.[1]
It could be special-cased to have some meaning, but it's not as straight-forward as it may seem at first. Even if we stick to freshly introduced "any lifetime" bindings there are sometimes multiple possibilities:
where &T: Trait
// where for<'a> &'a T: Trait
where &T: Trait<'_>
// where for<'a> &'a T: Trait<'a>
// -or-
// where for<'a, 'b> &'a T: Trait<'b>
where &Ref<'_, T>: Trait<'_>
// etc
But also because it could be confusing and easy to make mistakes in context:
The two bounds in the last code block put different restrictions on the caller and callee. But if &T: Trait desugared to for<'t> &'t T: Trait, the two bounds are only two easy-to-forget (and overlook) characters away from each other.
Being familiar with the current function lifetime elision rules, I'm not even sure which desugaring is more intuitive -- which probably means "neither is, so it's not a good candidate for sugar".
This has been covered by some commenters above, but I want to add that idea of the self receiver consuming or transferring ownership is just an approximation that some explanations will start with.
Your understanding could shift to the self receiver being a move without consideration for transfer of ownership. It was mentioned that &T is a Copy type, and the key to recognize here is that Copy types can always be moved. Coming full circle, when the trait is implemented on &T, the self receiver is &T.
If the implementing type is &T, then a &self receiver will be a &&T, yes. References compose like any other type (you can have a Vec<Vec<T>> too). I'm not sure what else to say about it.