Generic associated type may not live long enough - rust

Take the following example (Playground):
#![feature(generic_associated_types)]
#![allow(incomplete_features)]
trait Produce {
type CustomError<'a>;
fn produce<'a>(&'a self) -> Result<(), Self::CustomError<'a>>;
}
struct GenericProduce<T> {
val: T,
}
struct GenericError<'a, T> {
producer: &'a T,
}
impl<T> Produce for GenericProduce<T> {
type CustomError<'a> = GenericError<'a, T>;
fn produce<'a>(&'a self) -> Result<(), Self::CustomError<'a>> {
Err(GenericError{producer: &self.val})
}
}
The GenericError has a lifetime to allow it to take Produce as a reference. However, this code doesn't compile. It gives me the error:
error[E0309]: the parameter type `T` may not live long enough
--> src/lib.rs:19:5
|
18 | impl<T> Produce for GenericProduce<T> {
| - help: consider adding an explicit lifetime bound...: `T: 'a`
19 | type CustomError<'a> = GenericError<'a, T>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
This error makes sense to me because the GenericError doesn't have any restriction telling it that T must be 'a. I'm having trouble figuring out how to solve the problem though. Perhaps my generic lifetimes are misplaced?
The feature of the trait I wish to capture is that any Produce::CustomError should be able to capture self in a return. Perhaps I am going about this in the wrong way?

The same trait without generic_associated_types takes the lifetime in the trait itself.
trait Produce<'a> {
type CustomError;
fn produce(&'a self) -> Result<(), Self::CustomError>;
}
struct GenericProduce<T> {
val: T,
}
struct GenericError<'a, T> {
producer: &'a T,
}
impl<'a, T: 'a> Produce<'a> for GenericProduce<T> {
type CustomError = GenericError<'a, T>;
fn produce(&'a self) -> Result<(), Self::CustomError> {
Err(GenericError{producer: &self.val})
}
}
This tells the impl upfront that T that it must be 'a.

Related

Issues setting lifetimes in functions of Traits

I have a trait Atom that has many associated types, of which one is an owned version OP and the other is a borrow version O of essentially the same data. I have a function to_pow_view that creates a view from an owned version and I have an equality operator.
Below is an attempt:
pub trait Atom: PartialEq {
// variants truncated for this example
type P<'a>: Pow<'a, R = Self>;
type OP: OwnedPow<R = Self>;
}
pub trait Pow<'a>: Clone + PartialEq {
type R: Atom;
}
#[derive(Debug, Copy, Clone)]
pub enum AtomView<'a, R: Atom> {
Pow(R::P<'a>),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum OwnedAtom<R: Atom> {
Pow(R::OP),
}
pub trait OwnedPow {
type R: Atom;
fn some_mutable_fn(&mut self);
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a>;
// compiler said I should add 'a: 'b
fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
if self.to_pow_view().eq(&other) {
self.some_mutable_fn();
}
}
}
impl<R: Atom> OwnedAtom<R> {
// compiler said I should add 'a: 'b, why?
pub fn eq<'a: 'b, 'b>(&'a self, other: AtomView<'b, R>) -> bool {
let a: AtomView<'_, R> = match self {
OwnedAtom::Pow(p) => {
let pp = p.to_pow_view();
AtomView::Pow(pp)
}
};
match (&a, &other) {
(AtomView::Pow(l0), AtomView::Pow(r0)) => l0 == r0,
}
}
}
// implementation
#[derive(Debug, Copy, Clone, PartialEq)]
struct Rep {}
impl Atom for Rep {
type P<'a> = PowD<'a>;
type OP = OwnedPowD;
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct PowD<'a> {
data: &'a [u8],
}
impl<'a> Pow<'a> for PowD<'a> {
type R = Rep;
}
struct OwnedPowD {
data: Vec<u8>,
}
impl OwnedPow for OwnedPowD {
type R = Rep;
fn some_mutable_fn(&mut self) {
todo!()
}
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a> {
PowD { data: &self.data }
}
}
fn main() {}
This code gives the error:
27 | fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
| -- lifetime `'b` defined here
28 | if self.to_pow_view().eq(&other) {
| ------------------
| |
| immutable borrow occurs here
| argument requires that `*self` is borrowed for `'b`
29 | self.some_mutable_fn();
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
I expect this to work, since the immutable borrow should be dropped right after the eq function evaluates.
Something in the setting of the lifetimes is wrong in this code, already in the equality function eq: I would expect that there is no relation between 'a and 'b; they should just live long enough to do the comparison. However, the compiler tells me that I should add 'a: 'b and I do not understand why. The same thing happened for the function test.
These problems lead me to believe that the lifetimes in to_pow_view are wrong, but no modification I tried made it work (except for removing the 'a lifetime on &'a self, but then the OwnedPowD does not compile anymore).
Link to playground
Can someone help understand what is going on?
Here's the point: you constrained Pow to be PartialEq. However, PartialEq is PartialEq<Self>. In other words, Pow<'a> only implements PartialEq<Pow<'a>> for the same 'a.
This is usually the case for any type with lifetime and PartialEq, so why does it always work but not here?
It usually works because if we compare T<'a> == T<'b>, the compiler can shrink the lifetimes to the shortest of the two and compare that.
However, Pow is a trait. Traits are invariant over their lifetime, in other words, it must stay exactly as is, not longer nor shorter. This is because they may be used with invariant types, for example Cell<&'a i32>. Here's an example of how it could be exploited if this was allowed:
use std::cell::Cell;
struct Evil<'a> {
v: Cell<&'a i32>,
}
impl PartialEq for Evil<'_> {
fn eq(&self, other: &Self) -> bool {
// We asserted the lifetimes are the same, so we can do that.
self.v.set(other.v.get());
false
}
}
fn main() {
let foo = Evil { v: Cell::new(&123) };
{
let has_short_lifetime = 456;
_ = foo == Evil { v: Cell::new(&has_short_lifetime) };
}
// Now `foo` contains a dangling reference to `has_short_lifetime`!
dbg!(foo.v.get());
}
The above code does not compile, because Evil is invariant over 'a, but if it would, it would contain UB in safe code. For that reason, traits, that may contain types such as Evil, are also invariant over their lifetimes.
Because of that, the compiler cannot shrink the lifetime of other. It can shrink the lifetime of self.to_pow_view() (in test(), eq() is similar), because it doesn't really shrink it, it just picks a shorter lifetime for to_pow_view()'s self. But because PartialEq is only implemented for types with the same lifetime, it means that the Pow resulting from self.to_pow_view() must have the same lifetime of other. Because of that, (a) 'a must be greater than or equal to 'b, so we can pick 'b out of it, and (b) by comparing, we borrow self for potentially whole 'a, because it may be that 'a == 'b and therefore the comparison borrows self for 'a, so it is still borrowed immutably while we borrow it mutably for some_mutable_fn().
Once we understood the problem, we can think about the solution. Either we require that Pow is covariant over 'a (can be shrinked), or we require that it implements PartialEq<Pow<'b>> for any lifetime 'b. The first is impossible in Rust, but the second is possible:
pub trait Pow<'a>: Clone + for<'b> PartialEq<<Self::R as Atom>::P<'b>> {
type R: Atom;
}
This triggers an error, because the automatically-derived PartialEq does not satisfy this requirement:
error: implementation of `PartialEq` is not general enough
--> src/main.rs:73:10
|
73 | impl<'a> Pow<'a> for PowD<'a> {
| ^^^^^^^ implementation of `PartialEq` is not general enough
|
= note: `PartialEq<PowD<'0>>` would have to be implemented for the type `PowD<'a>`, for any lifetime `'0`...
= note: ...but `PartialEq` is actually implemented for the type `PowD<'1>`, for some specific lifetime `'1`
So we need to implement PartialEq manually:
impl<'a, 'b> PartialEq<PowD<'b>> for PowD<'a> {
fn eq(&self, other: &PowD<'b>) -> bool {
self.data == other.data
}
}
And now it works.

How to create a recursive queue?

I am trying to implement a recursive queue using the trait Element<T> as a container for all the functions of the different nodes of the queue and two structs implementing it called Node<T> and End.
The Node<T> struct is supposed to handle all the functionality within the queue itself and the End struct's purpose is to deal with the case of the last node.
I've got the following code:
trait Element<T> {
fn append_item(self, item: T) -> Node<T>;
}
struct Node<T> {
data: T,
successor: Box<dyn Element<T>>
}
impl<T> Element<T> for Node<T> {
fn append_item(mut self, item: T) -> Node<T> {
self.successor = Box::new(self.successor.append_item(item));
self
}
}
struct End;
impl<T> Element<T> for End {
fn append_item(self, item: T) -> Node<T> {
Node { data: item, successor: Box::new(self) }
}
}
The problem is, that I get two errors:
Cannot move a value of type dyn Element<T>
The parameter type T may not live long enough
both on the same line in Node::append_item.
Now, I get why the first error occurs (because the size of dyn Element<T> cannot be statically determined) but I don't know how to work around it and I have no idea why the second error occurs.
error[E0161]: cannot move a value of type `dyn Element<T>`
--> src/lib.rs:12:35
|
12 | self.successor = Box::new(self.successor.append_item(item));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the size of `dyn Element<T>` cannot be statically determined
The issue here is that fn append_item(self, item: T) takes self by value, but in this case self has type dyn Element<T>, which is unsized and can therefore never be passed by value.
The easiest solution here is to just take self: Box<Self> instead. This means that this method is defined on a Box<E> instead of directlThis will work perfectly fine with trait objects like dyn Element<T>, and it does not require the type to be sized.
(Additionally, in the updated code below, I've changed the return type to Box<Node<T>> for convenience, but this is not required per se, just a bit more convenient.)
trait Element<T> {
fn append_item(self: Box<Self>, item: T) -> Box<Node<T>>;
}
impl<T> Element<T> for Node<T> {
fn append_item(mut self: Box<Self>, item: T) -> Box<Node<T>> {
self.successor = self.successor.append_item(item);
self
}
}
impl<T> Element<T> for End {
fn append_item(self: Box<Self>, item: T) -> Box<Node<T>> {
Box::new(Node { data: item, successor: self })
}
}
[Playground link]
error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:12:26
|
12 | self.successor = Box::new(self.successor.append_item(item));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
The issue here is that dyn Trait has an implicit lifetime. In reality, it's dyn '_ + Trait. What exactly that lifetime is depends on the context, and you can read the exact rules in the Rust reference, but if neither the containing type nor the trait itself has any references, then the lifetime will always be 'static.
This is the case with Box<dyn Element<T>>, which really is Box<dyn 'static + Element<T>>. In other words, whatever type is contained by the Box must have a 'static lifetime. For this to be the case for Node<T>, it must also be that T has a 'static lifetime.
This is easy to fix, though: just follow the compiler's suggestion to add a T: 'static bound:
impl<T: 'static> Element<T> for Node<T> {
// ^^^^^^^^^
// ...
}
[Playground link)
Frxstrem already elaborated how to get around the need to move by using Box<Self> as receiver. If you need more flexibility in the parameter type (i.e. be able to hold on to references as well as owned types) instead of requiring T: 'static you can introduce a named lifetime:
trait Element<'a, T: 'a> {
fn append_item(self: Box<Self>, item: T) -> Node<'a, T>;
}
struct Node<'a, T: 'a> {
data: T,
successor: Box<dyn Element<'a, T> + 'a>,
}
impl<'a, T: 'a> Element<'a, T> for Node<'a, T> {
fn append_item(self: Box<Self>, new_data: T) -> Node<'a, T> {
Node {
successor: Box::new(self.successor.append_item(new_data)),
..*self
}
}
}
struct End;
impl<'a, T: 'a> Element<'a, T> for End {
fn append_item(self: Box<Self>, data: T) -> Node<'a, T> {
Node {
data,
successor: self,
}
}
}

Lifetime collision when bounding reference of a trait as IntoIterator

I tried to implement some graph algorithms on generic graphs. For that, I defined two graph traits which would return either a generic trait (having set-operations) SetGraph or an IntoIterator used to iterate over the nodes NeighborhoodIteratorGraph.
pub trait NeighborhoodIteratorGraph<'a> {
//which into_iterator do we have?
type IntoIter: 'a + std::iter::IntoIterator<Item = usize>;
fn get_neighborhood_iterator(&'a self, index: usize) -> Self::IntoIter;
}
pub trait SetGraph<'a>
where
&'a Self::S: IntoIterator<Item = usize>,
Self::S: 'a,
{
type S;
fn get_neighborhood(&'a self, index: usize) -> &'a Self::S;
}
Because one is usually able to iterate over sets, I also implemented NeighborhoodIteratorGraph for all SetGraph which are able to iterate over their sets.
impl<'a, G> NeighborhoodIteratorGraph<'a> for G
where
G: SetGraph<'a>,
&'a G::S: IntoIterator<Item = usize>,
{
type IntoIter = &'a G::S;
fn get_neighborhood_iterator(&'a self, index: usize) -> Self::IntoIter {
self.get_neighborhood(index)
}
}
I needed to add a lifetime to NeighborrhoodIteratorGraph otherwise the compiler would tell me my implementation would have an unbounded lifetime.
However I quicky run into problems with these lifetimes and I get an error for the following code:
struct Foo<'a, G: NeighborhoodIteratorGraph<'a>> {
graph: G,
//otherwise we get an error because 'a wouldn't be used
_marker: std::marker::PhantomData<&'a G>,
}
impl<'a, G: NeighborhoodIteratorGraph<'a>> Foo<'a, G> {
pub fn find_matching_for<I>(&mut self, nodes: I) -> bool
where
I: std::iter::IntoIterator<Item = usize>,
{
for node in self.graph.get_neighborhood_iterator(3) {}
return true;
}
}
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
It seems that the PhantomData field is more a hack and I can't find a way in which I get a set refernce which can be seen as a IntoIterator object.
Here is the Rust Playground of the problem.
Full error message:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:38:32
|
38 | for node in self.graph.get_neighborhood_iterator(3) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 34:5...
--> src/lib.rs:34:5
|
34 | / pub fn find_matching_for<I>(&mut self, nodes: I) -> bool
35 | | where
36 | | I: std::iter::IntoIterator<Item = usize>,
| |_________________________________________________^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:38:21
|
38 | for node in self.graph.get_neighborhood_iterator(3) {}
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 33:6...
--> src/lib.rs:33:6
|
33 | impl<'a, G: NeighborhoodIteratorGraph<'a>> Foo<'a, G> {
| ^^
note: ...so that the types are compatible
--> src/lib.rs:38:32
|
38 | for node in self.graph.get_neighborhood_iterator(3) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `&'a G`
found `&G`
What you want is a workaround for the lack of generic associated types, which are currently very unstable. Something Like
pub trait NeighborhoodIteratorGraph {
type IntoIter<'a>: std::iter::IntoIterator<Item = usize> + 'a;
fn get_neighborhood_iterator<'b>(&'b self, index: usize) -> Self::IntoIter<'b>;
}
would serve you perfectly if they were stable.
The first thing I did is remove the lifetime bound on NeighborhoodIteratorGraph and add it to the return type:
pub trait NeighborhoodIteratorGraph {
type IntoIter: std::iter::IntoIterator<Item = usize>;
fn get_neighborhood_iterator<'b>(&'b self, index: usize) -> Self::IntoIter
where
Self::IntoIter: 'b;
}
I then removed unnecessary lifetime annotations from SetGraph:
pub trait SetGraph<'a>
where
&'a Self::S: IntoIterator<Item = usize>,
Self::S: 'a,
{
type S;
fn get_neighborhood(&self, index: usize) -> &Self::S;
}
I then changed the blanket impl's signature to match the modified traits, and changed the impl from G to &'a G to properly constrain the lifetime 'a:
impl<'a, G> NeighborhoodIteratorGraph for &'a G
where
G: SetGraph<'a>,
&'a G::S: IntoIterator<Item = usize>,
{
type IntoIter = &'a G::S;
fn get_neighborhood_iterator<'b>(&'b self, index: usize) -> Self::IntoIter
where
Self::IntoIter: 'b,
{
self.get_neighborhood(index)
}
}
Because of those changes I was able to simplify Foo and its impl:
struct Foo<G: NeighborhoodIteratorGraph> {
graph: G,
}
impl<G: NeighborhoodIteratorGraph> Foo<G> {
pub fn find_matching_for<I>(&mut self, nodes: I) -> bool
where
I: std::iter::IntoIterator<Item = usize>,
{
for node in self.graph.get_neighborhood_iterator(3) {}
return true;
}
}
Leaving the compiler output with nothing but dead code warnings. Playground link

Storing FnMut in struct gives lifetime problems

I'm trying to store a FnMut in a struct:
struct OpenVPNSocket {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()>>>,
}
impl OpenVPNSocket {
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()>>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
I get this error:
error[E0310]: the parameter type `F` may not live long enough
--> src/lib.rs:8:42
|
7 | fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()>>(&mut self, callback: Box<F>) {
| -- help: consider adding an explicit lifetime bound...: `F: 'static +`
8 | self.socket_send_callback = Some(callback);
| ^^^^^^^^ ...so that the type `F` will meet its required lifetime bounds
I understand lifetime as something to do with references. However I don't use references. I don't see why my struct cannot store a Box. A Box lives as long as it's used.
UPDATE:
I have this example:
use std::sync::Arc;
pub type OnConsume = Arc<dyn Fn() -> Option<u8> + Send + Sync>;
struct Test {
callback: OnConsume
}
impl Test {
fn set_on_consume(&mut self, f: OnConsume) {
self.callback = f;
}
}
which works. What is the difference from the previous one?
In Rust, values also have lifetimes. Take, for example, this struct:
struct RefWrapper<'a> {
some_ref: &'a u32
}
An instance of RefWrapper is not a reference, but contains a lifetime. Since you're moving the box into the struct, which could live for the duration of the program (the method makes no guarantees as to when the struct instance could be dropped), the function must live for the maximum lifetime, the static lifetime.
All trait objects have lifetimes, the default implicit lifetime for boxed trait objects is 'static so your struct's socket_send_callback actually has an implicit + 'static bound. Shown in context:
struct OpenVPNSocket {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()> + 'static>>,
}
Since the boxed trait object has to be bounded by a 'static lifetime when you write a function to set this field the value itself must have a 'static lifetime which is why the compiler suggests adding that explicit bound. Fixed example with added bound:
impl OpenVPNSocket {
// notice the added 'static bound for F
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()> + 'static>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
With this change your code will compile. If you want to accept trait objects that aren't bounded by 'static lifetimes then you can do that by making your OpenVPNSocket generic over lifetimes. This alternative solution also compiles:
struct OpenVPNSocket<'a> {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()> + 'a>>,
}
impl<'a> OpenVPNSocket<'a> {
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()> + 'a>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
The reason why this code works is because you define the type once and use it in multiple places, and in all places it has the implicit 'static bound. Desugared:
use std::sync::Arc;
pub type OnConsume = Arc<dyn Fn() -> Option<u8> + Send + Sync + 'static>;
struct Test {
callback: OnConsume
}
impl Test {
fn set_on_consume(&mut self, f: OnConsume) {
self.callback = f;
}
}
However you can do the exact same thing in your prior code as well:
type Callback = Box<dyn FnMut(Vec<u8>) -> Result<(), ()>>;
struct OpenVPNSocket {
socket_send_callback: Option<Callback>,
}
impl OpenVPNSocket {
fn set_socket_send(&mut self, callback: Callback) {
self.socket_send_callback = Some(callback);
}
}
The above also compiles, and the implicit 'static bound is still there.

lifetime must be valid for the static lifetime so that the types are compatible

I'm aware of Where did the 'static lifetime come from and Cannot infer an appropriate lifetime for autoref due to conflicting requirements。
But I still do not understand the problem I've encountered:
use std::ops::Index;
trait Stack<T> {
fn as_slice(&self) -> &[T];
}
impl<T> Index<usize> for Stack<T> {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
trait Core {
fn stack(&self) -> &Stack<usize>;
fn bad(&mut self) -> usize {
self.stack()[0]
}
fn good(&mut self) -> usize {
self.stack().as_slice()[0]
}
}
fn main() {}
In the code above, good() gives no error, but bad() complains with:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:18:14
|
18 | self.stack()[0]
| ^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 17:5...
--> src/main.rs:17:5
|
17 | / fn bad(&mut self) -> usize {
18 | | self.stack()[0]
19 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:18:9
|
18 | self.stack()[0]
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the types are compatible:
expected std::ops::Index<usize>
found std::ops::Index<usize>
There is no Box in this code, and I do not know where the static lifetime comes from.
Edit: through try and error I found the compiler assume Stack + 'static. The following code compiles. But why? Please point me to some document.
impl<'b, T> Index<usize> for Stack<T> + 'b {
type Output = T;
fn index<'a>(&'a self, i: usize) -> &'a T {
&self.as_slice()[i]
}
}
You are implementing a Trait (Index) for a Trait Object (Stack<T>).
The Rust reference states:
Since a trait object can contain references, the lifetimes of those references need to be expressed as part of the trait object. This lifetime is written as Trait + 'a. There are defaults that allow this lifetime to usually be inferred with a sensible choice.
If You don't define a lifetime the compiler assume a default, in this case it is assumed 'static (see here for a detailed explanation)
Your code is equivalent to:
impl<T> Index<usize> for Stack<T> + 'static {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
To resolve the cannot infer an appropriate lifetime for autoref due to conflicting requirements compilation error just declare that stack() method returns a trait object with 'static lifetime.
trait Core {
fn stack(&self) -> &'static Stack<usize>;
fn bad(&mut self) -> usize {
self.stack()[0]
}
fn good(&mut self) -> usize {
self.stack().as_slice()[0]
}
}
Otherwise declare a generic lifetime for the Stack<T> trait object that impl Index:
impl<'a, T> Index<usize> for Stack<T> + 'a {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
trait Core {
fn stack(&self) -> &Stack<usize>;
fn bad(&mut self) -> usize {
self.stack()[0]
}
fn good(&mut self) -> usize {
self.stack().as_slice()[0]
}
}
At this point you should ask: why using as_slice() in good() works and using index() in bad() does not?
To make same sense of it try read the comments embedded in the MVCE below.
use std::ops::Index;
trait Stack<T> {
fn as_slice(&self) -> &[T];
}
// equivalent to: impl<T> Index<usize> for Stack<T>
// just to expose the conflicting requirements error
// the right declaration is:
// impl<'a, T> Index<usize> for Stack<T> + 'a
impl<T> Index<usize> for Stack<T> + 'static {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_slice()[i]
}
}
trait Core {
fn stack(&self) -> &Stack<usize>;
fn bad<'a>(&'a mut self) -> usize {
//self.stack()[0] syntactic sugar for:
*self.stack().index(0)
// self.stack() returns a trait object with a lifetime bound != 'static
// but Stack impl for Index requires a 'static lifetime bound:
// COMPILE ERROR: cannot infer an appropriate lifetime for
// autoref due to conflicting requirements
}
fn good<'a>(&'a mut self) -> usize {
// self.stack() returns a trait object with 'a lifetime bound
// this is congruent with as_slice() lifetime requirements
// see Catasta::as_slice() impl below
// NO COMPILE ERROR
self.stack().as_slice()[0]
}
}
struct Catasta<T> {
pila: [T;4]
}
impl<T> Stack<T> for Catasta<T> {
fn as_slice<'a>(&'a self) -> &'a [T] {
&self.pila
}
}
struct RealCore {
stk: Catasta<usize>
}
impl Core for RealCore {
fn stack(&self) -> &Stack<usize> {
&self.stk
}
}
fn main() {
let mut core = RealCore {stk: Catasta {pila: [100, 2, 3, 4]} };
println!("pos [0] item: {}", core.good());
}

Resources