I am trying to get a better understanding of Rust lifetimes.
struct Name<'a> {
arg: &'a u8,
}
impl<'a> Name<'a> {
fn new1(arg: &'a u8) -> Name<'a> {
Name { arg }
}
fn new2<'b>(arg: &'b u8) -> Name<'b> {
Name { arg }
}
}
Is there any difference between functions new1 and new2? I am assuming it would matter if arg was &self? Is there any case where new2 implementation preferred or the other way?
These two methods end up being exactly the same, but it's worth learning why. Along the way, we'll learn about lifetime coercion
One lifetime can be coerced into another lifetime if that second lifetime is shorter than (or rather, is contained by) the first. This is usually notated 'a: 'b, to mean that the lifetime 'a entirely encloses the lifetime 'b. The usual terminology is that 'a outlives 'b. The reasoning for this coercion is that you can always make a lifetime shorter if you need to. If a reference is valid during some lifetime, then it's also valid during any shorter lifetime contained in the longer lifetime.
So with that in mind, what sort of arguments can new1 and new2 take? We have a fixed lifetime 'a since the whole implementation is generic in that lifetime. However, new1 can not only take &'a u8, but any &'b u8 if 'b can be coerced to 'a. That is, 'b: 'a and 'b is longer than 'a.
new2 is slightly different, but it ends up having the same effect. The method is generic in the lifetime 'b and can take any &'c u8 if 'c: 'b. new2 is still technically generic in 'a, but since it doesn't use 'a at all, it can be ignored. That said, ignoring a generic parameter is confusing (why have the parameter at all?), so it would probably be best to use new1 instead.
Another reason to prefer new1 over new2 is that it fits better with Self. If we try to change the outputs to Self
impl<'a> Name<'a> {
fn new1(arg: &'a u8) -> Self {
Name { arg }
}
fn new2<'b>(arg: &'b u8) -> Self {
Name { arg }
}
}
the compiler complains. Why? Now the outputs have to be Name<'a> and in new2, we're returning Name<'b>. This isn't coercible to Name<'a> unless 'b: 'a, so we have to add that as a bound on 'b:
impl<'a> Name<'a> {
fn new1(arg: &'a u8) -> Self {
Name { arg }
}
fn new2<'b: 'a>(arg: &'b u8) -> Self {
Name { arg }
}
}
(playground link)
In this case, it's pretty clear that new1 is superior since it doesn't even need that second lifetime, but still allows exactly the same inputs.
Related
I want to create a wrapper around (nested) slices for easy operations on multidimensional data, owned by a different struct.
The most basic version of the mutable version of my slice wrapper might look like this:
struct MySliceMut<'a> {
data: Vec<&'a mut [f32]>,
}
impl<'a, 'b> MySliceMut<'a> {
fn get(&'b mut self) -> &'a mut [&'b mut [f32]] {
self.data.as_mut_slice()
}
}
Now if I want to implement a trait, for instance AddAssign, Rust does not seem to infer the lifetime of &mut self from the implementing type. The compiler complains that &mut self might outlive 'a:
impl<'a> AddAssign<MySlice<'a>> for MySliceMut<'a> { // lifetime 'a
fn add_assign(&mut self, rhs: MySlice<'a>) { // lifetime '1
let a = self.get(); // lifetime may not live long enough, '1 must outlive 'a
let b = rhs.get();
// do inplace addition here
}
}
Full Code - Rust Playground
I tried to figure out the issue with the lifetimes, but can't find it. Would the trait impl require any additional annotations?
struct MySlice<'a> {
data: Vec<&'a [f32]>,
}
impl<'a, 'b> MySlice<'a> {
fn get(&'b self) -> &'a [&'b [f32]] {
self.data.as_slice()
}
}
Problem with your code is that fn get(&'b self) returns variable with wrong lifetime. Associated lifetime 'a of MySlice<'a> is lifetime of inner slice. Associated lifetime 'b of fn get(...) is lifetime of the self. So I guess the function probably should return &'b [&'a [f32]] instead.
-- Edited --
Make sure to change fn get(...) of MySliceMut either.
Given the following condition:
/// Zero Sized Type
struct ZST;
/// What the ZST converts into
const TEXT: &str = "target text";
The following code snippets do the same thing, at least on the surface:
// static impl
impl From<ZST> for &'static str {
fn from(_: ZST) -> &'static str {
TEXT
}
}
// generic impl
impl<'a> From<ZST> for &'a str {
fn from(_: ZST) -> &'a str {
TEXT
}
}
My question is:
Are they exactly the same? If not, what is the difference?
Which version takes more time to compile?
Since the reference is immutable and in return position, these are identical. That is, you can assign a &'static str to a variable that requires a &'a str, for any lifetime 'a. Likewise, either of these can be assigned to a variable that requires a shorter lifetime because a reference with a longer lifetime is a subtype.
In fact, the only possible way to implement impl<'a> From<ZST> for &'a str is to return a static. That's because it has to work for any 'a, and the only lifetime that satisfies that is 'static.
As for a difference in compile time, it will never be significant enough to worry about.
I have a trait which says that any implementation of Foo needs to provide a method bar which returns an object of some type which implements Iterator<Item = u32>:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(&self) -> FooIterator;
}
For this case, I believe that the default lifetime elision means that the iterator returned by bar is required to live on its own, without being tied to the lifetime of the Foo it is iterating over. User Habnabit on #rust irc suggested the following way to say that the lifetime of the FooIterator is less than the lifetime of the Foo. i.e. it allows the implementation of the FooIterator to keep a reference to the Foo that it comes from:
trait Foo<'a> {
type FooIterator: Iterator<Item = u32> + 'a;
fn bar<'b: 'a>(&'b self) -> Self::FooIterator;
}
What I really want is the case where the function bar takes an additional argument, and the implementation of FooIterator is allowed to keep a reference to both the Foo and the additional argument. i.e. the lifetime of FooIterator is bounded by the lifetime of the Foo and the lifetime of the additional argument.
My literal translation of this idea would be
trait Zip {}
trait Foo<'a, 'c> {
type FooIterator: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'b: 'a, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator;
}
But I was told there there is no "good" way to do this. What would be the best way to implement this idiom? What would the above code do exactly?
What you're looking for is associated type constructors, a planned feature that is not yet implemented in Rust. With associated type constructors, your code would look like this:
trait Zip {}
trait Foo {
type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'a, 'b: 'a, 'c, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator<'a, 'c>;
}
Actually, I'm not sure all those lifetimes are necessary, because a &'a T can be coerced to a &'b T where 'a: 'b. Thus, the following might be good enough:
trait Zip {}
trait Foo {
type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'a, 'c>(&'a self, &'c Zip) -> Self::FooIterator<'a, 'c>;
}
Depending on how you want to use this trait, you may be able to make it work by implementing it for &'a Struct instead of for Struct, thus "hoisting" the responsibility for finding the right lifetime from the trait into the caller.
Remove the lifetime annotation from the trait and change bar so it takes self, plus another argument of the same lifetime:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(self, other: Self) -> Self::FooIterator;
}
(Removing 'a from the trait is possible because bar consumes the reference instead of reborrowing it -- self doesn't have to outlive the return value anymore because it's been moved into it.)
Then impl it for a reference of lifetime 'a:
impl<'a> Foo for &'a Vec<u32> {
type FooIterator = ...; // something presumably containing 'a
fn bar(self, other: Self) -> Self::FooIterator {
...
}
}
This works because the compiler can limit the lifetime 'a to one for which the impl applies.
Here's a playground link where bar is basically a wrapper around .chain().
I'm ignoring the Zip trait for now because how to incorporate it depends on what it provides. Instead, I suppose that bar only accepts an argument of the same type as Self. However, you can probably add it as well, maybe using the same technique if you need to.
Can anyone tell what the lifetime error is in the following code? (simplified from my actual code) I've looked it over myself, but I can't figure out what is wrong or how to fix it. The problem comes when I try to add the Cell, but I'm not sure why.
use std::cell::Cell;
struct Bar<'a> {
bar: &'a str,
}
impl<'a> Bar<'a> {
fn new(foo: &'a Foo<'a>) -> Bar<'a> { Bar{bar: foo.raw} }
}
pub struct Foo<'a> {
raw: &'a str,
cell: Cell<&'a str>,
}
impl<'a> Foo<'a> {
fn get_bar(&self) -> Bar { Bar::new(&self) }
}
The compiler error is
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/foo.rs:15:32
|
15 | fn get_bar(&self) -> Bar { Bar::new(&self) }
| ^^^^^^^^
First, the solution:
use std::cell::Cell;
struct Bar<'a> {
bar: &'a str,
}
impl<'a> Bar<'a> {
fn new(foo: &Foo<'a>) -> Bar<'a> { Bar{bar: foo.raw} }
}
pub struct Foo<'a> {
raw: &'a str,
cell: Cell<&'a str>,
}
impl<'a> Foo<'a> {
fn get_bar(&self) -> Bar<'a> { Bar::new(&self) }
}
There are two problems in your code. The first is with get_bar, where you didn't specify the lifetime for the return type. When you don't specify lifetimes in signatures, Rust doesn't infer the correct lifetimes, it just blindly fills them in based on simple rules. In this specific case, what you get is effectively fn get_bar<'b>(&'b self) -> Bar<'b> which is obviously wrong, given the lifetime of self.raw (which was what you actually wanted) is 'a. See the Rust Book chapter on Lifetime Elision.
The second problem is that you're over-constraining the argument to Bar::new. &'a Foo<'a> means you require a borrow to a Foo for as long as the strings it's borrowing exist. But borrows within a type have to outlive values of said type, so the only lifetime valid in this case is where 'a matches the entire lifetime of the thing being borrowed... and that conflicts with the signature of get_bar (where you're saying &self won't necessarily live as long as 'a since it has its own lifetime). Long story short: remove the unnecessary 'a from the Foo borrow, leaving just &Foo<'a>.
To rephrase the above: the problem with get_bar was that you hadn't written enough constraints, the problem with Bar::new was that you'd written too many.
DK explained which constraints were missing and why, but I figured I should explain why the code was working before I added Cell. It turns out to be due to variance inference.
If you add in the inferred lifetimes to the original code and rename the lifetime variables to be unique, you get
struct Bar<'b> {
bar: &'b str,
}
impl<'b> Bar<'b> {
fn new(foo: &'b Foo<'b>) -> Bar<'b> { Bar{bar: foo.raw} }
}
pub struct Foo<'a> {
raw: &'a str,
cell: Cell<&'a str>,
}
impl<'a> Foo<'a> {
fn get_bar<'c>(&'c self) -> Bar<'c> { Bar::new(&self) }
}
The problem comes when calling Bar::new, because you are passing &'c Foo<'a>, to something expecting &'b Foo<'b>. Normally, immutable types in Rust are covariant, meaning that &Foo<'a> is implicitly convertable to &Foo<'b> whenever 'b is a shorter lifetime than 'a. Without the Cell, &'c Foo<'a> converts to &'c Foo<'c>, and is passed to Bar::new wiht 'b = 'c, so there is no problem.
However, Cell adds interior mutability to Foo, which means that it is no longer safe to be covariant. This is because Bar could potentially try to assign a shorter lived 'b reference back into the original Foo, but Foo requires that all of the references it holds are valid for the longer lifetime 'a. Therefore, interior mutability makes &Foo invariant, meaning you can no longer shorten the lifetime parameter implicitly.
This code passes the compiler (for clarification lifetimes are not elided):
struct Foo<'a> {
_field: &'a i32,
}
fn test<'a, 'b, 'c>(_x: &'a mut Foo<'c>, _y: &'b bool) { // case 1
}
fn main() {
let f = &mut Foo { _field: &0 };
{
let p = false;
test(f, &p);
}
}
If I use 'b instead of 'c in test's definition like so:
fn test<'a, 'b>(_x: &'a mut Foo<'b>, _y: &'b bool) { // case 2
}
the code fails to compile ("p does not live long enough")!
What I would expect to happen at the call of test in case 2 is:
'a is set to the actual lifetime of f,
'b is set to the intersection of the Foo's actual lifetime and &p's actual lifetime which is &p's lifetime,
and everything should be fine, as in case 1.
Instead, what actually seems to happen in case 2 is that 'b is forced to become the lifetime of the Foo which is too big for &p's lifetime, hence the compiler error 'p does not live long enough'. True?
Even stranger (case 3): this only fails if test takes a &mut. If I leave the <'b> in, but remove the mut like so:
fn test<'a, 'b>(_x: &'a Foo<'b>, _y: &'b bool) { // case 3
}
the code passes again.
Anyone to shed light on this?
Cheers.
Noting the difference with mut was a key observation. I think that it will make more sense if you change the type of the second argument and give one possible implementation:
fn test<'a, 'b>(_x: &'a mut Foo<'b>, _y: &'b i32) {
_x._field = _y;
}
This function has the ability to mutate _x. That mutation also includes storing a new reference in _field. However, if we were able to store a reference that had a shorter lifetime (the intersection you mentioned), as soon as the inner block ended, the reference in the Foo would become invalid and we would have violated Rust's memory safety guarantees!
When you use an immutable reference, you don't have this danger, so the compiler allows it.
You have discovered an important thing - Rust doesn't always care what you do in the function. When checking if a function call is valid, only the type signature of the function is used.
I'm sure there's a fancy way of saying this using the proper terms like contravariance and covariance, but I don't know those well enough to use them properly! ^_^