Why does bottom one do not work while top one does work and why does it not allow doing trait bound of e.g T: core::ops::Index<usize, &’a some_type> to trigger E0582
The function declaration you wrote takes the form:
fn function_name<'generic_lifetime, GenericType>(argument_name: GenericType)
where
GenericType: Constraint,
{
// Do nothing, or something!
}
The <'generic_lifetime, GenericType> declares the generic parameters (in this case, a lifetime and a type) which can then be used elsewhere in the signature. For functions, declarations of generic parameters go exactly where you put them here.
The impl block you wrote takes the form:
impl<GenericType> ParameterizedType<GenericType>
where
for<'generic_lifetime> Constraint,
{
fn function_name(argument_name: GenericType) -> GenericType {
// Do nothing, or something!
}
}
The first issue is that you're trying to use 'generic_lifetime, but you didn't declare it! Just like GenericType, it needs to be declared at the start of the impl, like so:
// Generic lifetimes are declared before generic types! The compiler will usually
// know if you did this wrong.
impl<'generic_lifetime, GenericType> // Rest unchanged
The second issue is that you're trying to constrain your implementation in the wrong way. You can constrain it one of two ways:
1: In the declaration
impl<'generic_lifetime, GenericType: Constraint> // Rest unchanged
2: with a where clause, which is what you seem to have tried to do
impl<'generic_lifetime, GenericType> ParameterizedType<GenericType>
where
GenericType: Constraint,
{ // Rest unchanged
The problem is the for<'a>/for<'generic_lifetime> in your where clause, as this isn't the correct syntax. Once your lifetime is declared at the start, you can just use it normally in the constraint definition. The only time for shows up in an impl is when implementing a trait, like so:
// Concrete trait on concrete type
impl Trait for Type {}
// Concrete trait on generic type
impl<GenericType> Trait for GenericType {}
// etc...
So the correct version of what you wrote is either:
impl<'a, T> A<T>
where
T: core::ops::Index<usize, Output = &'a str>,
{
fn arg(a: T) -> T {
a
}
}
or
impl<'a, T: core::ops::Index<usize, Output = &'a str>> A<T> {
fn arg(a: T) -> T {
a
}
}
Edit: Not sure why the posts are being flagged. I didn't even get to read that reply. But, anyway, I also just remembered for<'lifetime> is also used in higher-rank trait bounds, but I don't think that's applicable here. Though the syntax for that is similar to what you were doing, so that might have been the cause of confusion.
This the same as temp_user but I was silenced for now as such by staff provider; letting you know as so my reply would be very later for main account this is throwaway account for this circumstance
The bottom one doesn't work:
impl<T> A<T>
where
for<'a> T: core::ops::Index<usize, Output = &'a str>
Because that implementation is impossible to write. For any given type T, there is at most one implementation of Index<usize>, and in that implementation, <T as Index<usize>>::Output must resolve to a single type. But you're saying it must resolve to an infinite number of types, &'1 str, &'2 str, ...
Or to put it another way, if you try to write your own implementations...
impl Index<usize> for StructWithNoGenerics {
// You have to name the lifetime and `'static` is the only name here
type Output = &'static str;
}
impl Index<usize> for StructWith<'l1, 'l2> {
// You have to name the lifetime and the only names here are
// `'static`, `'l1`, and `'l2`
type Output = &'l2 str;
}
...there's no way to name more than one lifetime, and that lifetime has to be 'static or a lifetime appearing in the implementing type.[1] There's no way to name "all lifetimes", which is what the for<'a> bound would require.
It's plausible that the compiler could accept the code but everything in the impl would be uncallable[2] since the bound is unsatisfiable.[3] There are reasons that can be desirable[4] (though preferably with a warning lint), but it is usually not what you want.
The one that works:
fn arge<'a, E>(_a: E)
where
E: core::ops::Index<usize, Output = &'a str>,
{
}
Works because the caller will choose a single lifetime for 'a (and a single type for E), and -- provided 'a is either 'static or 'a is part of E's type -- there could be an implementation.
Note that like Deref, the Index trait presumes you own some instance of the Output type and you're going to return a reference to it in the implementation. Then there's a desugaring...
let item = collection[0];
// Roughly the same thing
let item = *<_ as Index<usize>>::index(&collection, 0);
...that dereferences the return from the implementation.
Your bound where Output is a &str can only work if the implementing type owns &strs, or if it returns references to some static (like a "literal") or leaked &strs.
// The outer lifetime of the `&'outer &'inner str` is some local borrow of
// `a`, and may be different on different calls, but the inner lifetime of
// the return is always the same -- whatever single lifetime appeared in the
// implementation.
let s /* : &&str */ = &a[0];
let s /* : &&str */ = <_ as Index<usize>>::index(&collection, 0);
let s /* : &str */ = a[0];
let s /* : &str */ = *<_ as Index<usize>>::index(&collection, 0);
Maybe that is what you intended. But maybe you intended to write a bound where the return in the implementation is a &str. That would mean Output is str instead:
where
T: core::ops::Index<usize, Output = str>,
// ...
let s /* : &str */ = &a[0];
let s /* : &str */ = <_ as Index<usize>>::index(&collection, 0);
// The other two won't compile because `str` is not `Sized`
This can return &strs with different lifetimes -- not because of a higher ranked bound, but because of the signature of Index::index. Or in other words, now the outer (and only) lifetime is on the &str, and corresponds to some borrow of a.
It was all automatic flags.[1] The playground-link-only post (second post in the thread) was held up by the automatic spam detection but I can’t really for sure know why the system thought it would be problematic (really, it better shouldn’t ever prevent such a post). And then the follow-up with a reply from a new account was caught by a mechanism that flags new “sockpuppet” accounts (replies that are from detected alt-accounts of the topic-starter; to be able to notice if people are ever faking engagement/conversations).
@temp_user, please excuse the delay, it can sometimes take a few hours until we get to reviewing flags. You should be able to respond to any of the answers if you want to ![]()
as well as automatic silencing from the automatic spam flags ↩︎
Hello I can reply now; so what your saying its that this implementation is impossible due to consider the lifetimes within the body of function must be considered; does this go with also rust’s golden rule with function signature being always correct
There was also mentions using for<‘a> did something, I still don’t get alot about lifetimes for example e.g what differs from how long it lives for Trait A<‘a> {} to a Struct<‘a> to a borrow in function signature such as fn borrow<‘a>(arg: &’a str) also it going off topic or not
Can anyone help me explain to me at the followup questions or should I wait until I can create new topic for it
You replied to steffahn, so I'm not sure if that's a question with regard to my answer or @quinedot's answer. I also haven't heard of this "golden rule" before, but that might just be me. I'm going to assume quinedot's answer for now because they tend to provide really good answers (and I don't entirely understand your question).
In general, Trait<'a>, Struct<'a>, and fn borrow<'a>(arg: &'a str) are all lifetime generics. The <'a> declares the 'a lifetime, which is filled in by the caller/user (in some cases, automatically). That lifetime is then associated with some reference or other lifetime-requiring item.
Personally, I recommend but what is 'a lifetime? by leddoo. I think it does a good job of making lifetimes more intuitive.
In general, do not think of Rust lifetimes ('_ things) as the liveness scope of a value. Instead they are a type-level property that typically corresponds to the duration of a borrow.
As part of the type system, lifetimes play many roles in Rust, and I've written too much below to be absorbed in one go. So let me reemphasize what lifetimes usually are, especially when you're just starting out with Rust: They are the duration of a borrow. Typically of a short-term borrow.
&place creates a shared borrow and &mut place creates an exclusive borrow. It's UB to have two exclusive borrows to the same place (even if no mutation occurs). Perhaps think of the &mut _ as "mutually exclusive".
With that context, let's look at some particular cases you asked about. Click to expand or collapse each section.
Function signatures
// A borrow in a function signature
fn show<'a>(arg: &'a str) { ... }
// Same thing:
// fn show(arg: &str) { ... }
When a function has a generic, like the generic 'a lifetime here, we say "the caller chooses the parameter's 'value'". They can pick any lifetime that meet any constraints on the function. There's always an implicit constraint that the lifetime[1] is valid until the call is complete. For this example, that's the only constraint on 'a.
From the caller's perspective, that means they can pass in a &str of any borrow duration at all in this case (they cannot "name" any borrows that do not cover the entire call to show).
From the callee's perspective, that means the borrow could be any duration strictly larger than the function body. It's impossible to borrow a variable local to the function for 'a, because all local variables are moved or go out of scope by the time the call is complete, and it's an error for something which is borrowed to be moved or go out of scope. You also can't assume the 'static lifetime or any other particular "long enough" lifetime in this case.
// A borrow in a function signature -- which returns the same lifetime
fn trim<'a>(arg: &'a str) -> &'a str { ... }
// Same thing:
// fn trim(arg: &str) -> &str { ... }
The meaning of this signature, where the input and output lifetimes are the same, means that using the returned value keeps the input borrow active. Example.
Here is a reference about lifetime elision in function and method signatures.
// Similar to last time but the input borrow is exclusive now
fn trim_and_stuff<'a>(arg: &'a mut String) -> &'a str { ... }
// Same thing:
// fn trim_and_stuff<'a>(arg: &'a mut String) -> &'a str { ... }
The meaning of this signature is also that using the returned value keeps the input borrow active. The input borrow is exclusive this time, though -- even though the return type is a shared reference. And here's an example of why this matters.
Traits
I'm putting this first so I can refer to it later. But as a beginner you probably won't work with lifetime-parameterized traits too much. They might come up with serde, but even there, you probably want DeserializedOwned to avoid the lifetime complications.
You probably don't want lifetimes on your own traits when starting out. Get the hang of lifetimes more generally first.
But anyway, here we go:
trait A<'a> { ... }
This usually means the ability to work with some borrow of duration 'a. For example, the sealed/unstable Searcher<'a> trait represents the ability to look for substrings in a &'a str. The implementors all hold onto the &'a str "haystack" in one way or another.
However, it can also be used as a sort of indirect representation of a generic type (a type constructor). So you could have a trait like...
trait BorrowSomething<'a> {
type Something;
fn ret_something(&'a self) -> Self::Something;
}
impl<'a, T: ?Sized> BorrowSomething<'a> for Mutex<T>
where
T: 'a,
{
type Something = MutexGuard<'a, T>;
fn ret_something(&'a self) -> Self::Something {
self.lock().unwrap()
}
}
The lifetime can be said to represent the ability to borrow something for 'a, but we also need to be able to name MutexGuard<'a, T>. And another implementation might need to name a different generic type, like a Ref<'a, T> or something. As opposed to Deref where every implementation returns a reference.
Today you would might use a GAT instead...
trait BorrowSomething {
type Something<'a>
where
Self: 'a;
fn ret_something(&self) -> Self::Something<'_>;
}
impl<T: ?Sized> BorrowSomething for Mutex<T> {
type Something<'a>
= MutexGuard<'a, T>
where
Self: 'a;
fn ret_something(&self) -> Self::Something<'_> {
self.lock().unwrap()
}
}
...but we didn't always have GATs, and there are some limitations in the language where GATs fall short which the lifetime parameterized trait pattern can still fill. (I won't go into it here.)
Structs
First, a heads-up: It's a common newcomer mistake to use lifetimes on structs so that you can have non-'static references in structs, when you should not. Rust references (&_, &mut _) are typically short-term borrows, and not something you would use in a long-lived data structure.
That being said, let's consider:
struct S<'a> {
some_borrow: &'a str,
some_dyn: Box<dyn BorrowSomething<'a, Something = &'a str> + 'a>,
}
The lifetime parameter usually means you are holding onto a borrow of duration 'a, like some_borrow. In fact, that's what it means the vast majority of time, and often that borrow is a reference (&'a _, &'a mut _) of some sort. Using values of that type will keep the associated borrow active, like they do with references.
More generally it means you're holding onto something with 'a in its type. The some_dyn value may hold onto some &'a str or &'a String or such... or it might hold onto a String and not a borrow.
That is: since a lifetime in a trait can represent the ability to work with a given borrow duration,[2] and dyn types reflect values that can be used with those traits and can be put into fields, a lifetime on a struct may also indicate the ability to work with a given borrow duration.
However, again, it almost always just means there's a borrow of duration 'a in S<'a> somewhere. The other possibilities are a reflection of the fact that lifetimes are part of Rust's type system, and can show up in roles other than in a borrowed value directly.
Higher ranked (for<..>) bounds
Finally you asked about for<'a>. If you have some trait bound like this:
fn foo<'a, T: 'a + BorrowSomething<'a, Something = &'a str>>(t: T) { ... }
(I'm reusing an example from above. Or see the playground link below to see the full trait.)
It means, "the caller chooses an 'a and T such that T implements BorrowSomething<'a> where the associated type is &'a str, and supplies a value of type T". Or more practically, "I can use the ret_something method to turn &'a T into &'a str".
The function body can't assume that T implements anything else -- including implementing BorrowSomething<'x> for some lifetime 'x that isn't 'a.
But sometimes you need the ability to work with every lifetime, not just some particular lifetime of the caller's choosing. That's where HRTBs -- for<'a> bounds -- come in:
fn bar<T: for<'x> BorrowSomething<'x, Something = &'x str>>(t: T) { ... }
This one means "T implements BorrowSomething<'x> where the associated type is &'x str, for every lifetime 'x". "I can use ret_something on a &T of any borrow duration to get a &str (of the same borrow duration)".
Recall that caller-chosen lifetimes, like the 'a on foo, are always longer than the function body. that means you can't actually make use of t.ret_something() in foo -- it would require borrowing t for 'a, which is longer than the function body. When we need some ability for a lifetime shorter than the function body -- a duration that you could borrow a local variable for -- we use HRTBs to express that need.
You can call t.ret_something() in bar because T has that ability for all lifetimes, including those too short for the caller of bar to name -- durations shorter than the function body.
Here's the playground example.
You can also have higher-ranked types, usually dyn types:
struct S {
some_dyn: Box<dyn for<'a> BorrowSomething<'a, Something = &'a str>>,
}
// Without elision that is a:
// ```
// dyn 'static + for<'a> BorrowSomething<'a, Something = &'a str>
// ```
// And whatever was coerced to `dyn Whatever` must meet a `Whatever` bound,
// so we must have had some
// ```
// bx: Box<T> // where:
// // T: 'static + for<'a> BorrowSomething<'a, Something = &'a str>
// ```
(Function pointers like for<'a> fn(&'a str)[3] are the other higher-ranked types.)
It was a very broad question
. When you come across particular questions you can create a new topic for it. If you have questions above the above data dump, it may be better to reply to individual pieces separately (though I recognize you're probably still post-rate limited).