Help regarding lifetime

#1

Dear all,
I am quite new in Rust (coming from python and C++), and I have some issue regarding lifetime.
I tried to implement a very simple program (see bellow) that has same structure that my program. Any help on how I must implement lifetime in this example will be very welcome (and if possible with explaination, so I will be able to understand, and not request help anymore :slight_smile: )

Regards,

Julien



//////////////////////////////////////////
pub struct Rec<'a> {
    pub s: Option<&'a Box<Trait1>>
}

impl<'a> Rec<'a> {
    pub fn new() -> Rec<'a> {
        Rec{s: None}
    }
}

pub trait Trait2<'a> {
    fn function_2(&self, rec: &'a mut Rec) -> bool;
}

/////////////////////////////////////////
pub struct Data<'b> {
    m: &'b Box<Trait1>
}

impl<'b> Data<'b> {
    pub fn new(m: &'b Box<Trait1>) -> Data {
        Data { m: m }
    }
}

impl<'c> Trait2<'c> for Data<'c> {
    fn function_2(&self, r: &'c mut Rec) -> bool {
        r.s = Some(self.m);
        true
    }
}

/////////////////////////////////////////
pub struct Struct {
    a: i32
}

impl Struct {
    pub fn new(a: i32) -> Struct {
        Struct{a: a}
    }
}

impl Trait1 for Struct {
    fn function_1(&self, rec: &Rec) -> bool {
        true
    }
}

///////////////////////////////////////////
pub struct Struct1 {
        
}

impl Struct1 {
    pub fn get_struct1(a: i32) -> Box<Trait1> {
        let struc = Struct::new(a);
        Box::new(struc)
    }
}

pub trait Trait1 {
    fn function_1(&self, rec: &Rec) -> bool;
}

///////////////////////////////////////////
pub struct Exec {
    e: i32
}

impl Exec {
    pub fn new(e: i32) -> Exec {
        Exec {e: e}
    }
    
    pub fn function_exec(&self, w: &Data) -> bool {
        let mut rec: Rec = Rec::new();
        
        w.function_2(&mut rec)
    }
}


//////////////////////////////////////////
fn main() {
    let exec = Exec::new(1);
    
    let s_1 = Struct1::get_struct1(2);
    let data : Data = Data::new(&s_1);
    
    exec.function_exec(&data);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
  --> src/main.rs:31:15
   |
31 |         r.s = Some(self.m);
   |               ^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime 'c as defined on the impl at 29:6...
  --> src/main.rs:29:6
   |
29 | impl<'c> Trait2<'c> for Data<'c> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:31:20
   |
31 |         r.s = Some(self.m);
   |                    ^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #2 defined on the method body at 30:5...
  --> src/main.rs:30:5
   |
30 | /     fn function_2(&self, r: &'c mut Rec) -> bool {
31 | |         r.s = Some(self.m);
32 | |         true
33 | |     }
   | |_____^
   = note: ...so that the expression is assignable:
           expected std::option::Option<&std::boxed::Box<(dyn Trait1 + 'static)>>
              found std::option::Option<&std::boxed::Box<dyn Trait1>>

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

#2

Is there some reason you’re doing double-indirection on your trait objects? Why are you storing &'a Box<dyn Trait1> rather than &'a dyn Trait1? Also, be aware that boxed trait objects by default are constrained by the 'static lifetime, so you might be wanting to use Box<Trait1 + 'a> instead to loosen that constraint. But I don’t know how to reconcile that with the lifetime of the borrow you’re storing, which is why I asked the first question. It looks wonky to me, but I haven’t looked that closely.

Specifically, I think the problem you’re having is that the &'b Box<Trait1> in Data requires the boxed object to have a 'static lifetime, but you’re allocating it within main, and it only lives as long as the Struct1 that owns it. Try making Data generic over the lifetime of the trait object by storing something like &'b Box<Trait1 + 'c>.

Compare to this version which compiles.

#3

Box is a pointer, so &Box is equivalent of C++'s Trait**, except & also locks it to be read-only.

References are locks that make structs containing them locked to a temporary scope. They don’t act like pointers. As a guideline, use only owned types in structs, and only references in function args. Other cases are relatively rare, and require more practice with ownership semantics.

#4

When beginning with Rust notions, follow @kornel advice.

Now, regarding your piece of code, instead of &'a Rec (i.e., &'a Rec<'_>) you want &'_ Rec<'a>.

Add #![deny(elided_lifetimes_in_paths)], it will help you see where you forgot to annotate structs lifetime parameters (non-annotated lifetime parameters in method arguments let Rust use independent lifetime parameters instead of the ones required).

I also recommend using #![deny(bare_trait_objects)] to help distinguish between types and traits:

#![deny(
    bare_trait_objects,
    elided_lifetimes_in_paths,
)]

//////////////////////////////////////////
#[derive(Default)]
pub
struct Rec<'a> {
    pub
    s: Option<&'a dyn Trait1>
}

impl<'a> Rec<'a> {
    pub
    fn new () -> Rec<'a>
    {
        Rec::default()
    }
}

pub
trait Trait2<'a> {
    fn function_2 (self: &'_ Self, rec: &'_ mut Rec<'a>) -> bool;
}

/////////////////////////////////////////
pub
struct Data<'a> {
    m: &'a dyn Trait1,
}

impl<'a> Data<'a> {
    pub
    fn new (m: &'a dyn Trait1) -> Data<'a>
    {
        Data {
            m /*: m*/,
        }
    }
}

impl<'a> Trait2<'a> for Data<'a> {
    fn function_2 (self: &'_ Self, r: &'_ mut Rec<'a>) -> bool
    {
        r.s = Some(self.m);
        true
    }
}

/////////////////////////////////////////
pub
struct Struct {
    a: i32
}

impl Struct {
    pub
    fn new (a: i32) -> Struct
    {
        Struct {
            a /*: a*/,
        }
    }
}

impl Trait1 for Struct {
    fn function_1 (self: &'_ Self, rec: &'_ Rec<'_>) -> bool
    {
        true
    }
}

///////////////////////////////////////////
pub
struct Struct1 {
        
}

impl Struct1 {
    pub
    fn get_struct1 (a: i32) -> Box<dyn Trait1>
    {
        let struc = Struct::new(a);
        Box::new(struc)
    }
}

pub
trait Trait1 {
    fn function_1 (self: &'_ Self, rec: &'_ Rec<'_>) -> bool;
}

///////////////////////////////////////////
pub
struct Exec {
    e: i32
}

impl Exec {
    pub
    fn new (e: i32) -> Exec
    {
        Exec {
            e /*: e*/,
        }
    }
    
    pub
    fn function_exec (self: &'_ Self, w: &'_ Data<'_>) -> bool
    {
        let mut rec: Rec<'_> = Rec::new();
        
        w.function_2(&mut rec)
    }
}


//////////////////////////////////////////
fn main ()
{
    let exec = Exec::new(1);
    
    let s_1: Box<dyn Trait1> = Struct1::get_struct1(2);
    let data: Data<'_> = Data::new(&*s_1); // get rid of one indirection
    
    exec.function_exec(&data);
}

This does compile.

3 Likes
#5

Thanks for your replies.

I miss the fact Box is already a pointer, so I will have to change that.
I will try to follow your advices, and I will have a deeper look at your code sample.

Thanks :slight_smile:

1 Like