in the code shown in this playground, gives the error
6 | data.sort_by_key(|d| &d.this);
| -- ^^^^^^^ returning this value requires that `'1` must outlive `'2`
| ||
| |return type of closure is &'2 String
| has type `&'1 S`
At first blush, I'd expect lifetime elision rules to take care of this by making the two lifetimes the same.
So I want to try writing the lifetimes explicitly, at which point I realize that I don't recall ever having had the need to add lifetime annotations to closure definitions before. Is it possible?
So I try to replace the closure with the function
fn key(item: &S) -> &String {
&item.this
}
which gives the error
7 | data.sort_by_key(key);
| ^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected reference `&String`
found reference `&String`
which appears to be drawing my attention to two things which are both rendered as &String, being somehow different.
The problem is that (I believe) sort_by_key temporarily stores the result of the key function, which means it can't reference the item (since the item is about to be moved during sorting). You'll notice the signature of the method doesn't allow for borrows coming from the input reference.
A reference from the surrounding scope would (I believe) satisfy it, hence why the borrow checker tells you yours doesn't live long enough. '2 is, essentially by definition, anything but'1.
(note: it's currently 1am where I live, and I'm running on four braincells and vague recollections of running into the same thing a while ago, so I apologize in advance for anything that's unclear or inaccurate)
Can you post a minimal example that reproduces the error?
So far in my experience when the compiler says
expected reference `SomeType`
found reference `SomeType`
and you cannot see any difference between the types it has been because they were from two different versions of the same crate. But I don't know how that would happen in the standard librery. On that note, is the String here the std String or some type that just has the same name?
Also it is very well possible that this is the same problem as in the closure case but the error isn't aware of differences in lifetimes.
Because there's no way to use the lifetime on the reference, therefore no way to annotate K with it. for<'a> Fn(&'a T) -> &'a K is a different, incompatible signature.
Exactly. If that weren't true, what would happen? sort_by_key would be allowed to get the key, then move, drop, modify - do whatever it wanted to the item, likely invalidating the reference in doing so. Because it can't see that the two types are at all related.
that means that K has to be the same type, no matter what the input lifetime is -- and types that differ by lifetime are still distinct types. So there is no way to be a closure that returns a type which contains the input lifetime and meet this signature.
The Iterator trait has the same sort of restriction:
type Item; // Must resolve to a single type
fn next(&mut self) -> Option<Self::Item>;
If you've ever heard stuff like "iterators can't borrow from their fields" or "you would need a lending iterator (that does support borrowing on next)," this is the restriction they were talking about.