takehiro iyatomi
takehiro iyatomi

Reputation: 823

how to annotate trait implemetation which returns Self, has specified lifetime?

title seems to be similar with Lifetime parameter for `Self` in trait signature but I create this new post because I believe root cause is different.

I have a trait like following

trait T<'a> {
    fn new(y: &'a Y) -> Self where Self: Sized;
    fn do_something(&self);
}

and want to write generic function that accept type X (which implements trait T<'a>), and reference of Y, then create dyn Trait T<'a>.

fn create_t_from_i<'a, I: T<'a>>(y: &'a Y) -> Box<dyn T<'a>> 

my naive implementation is like this

fn create_t_from_i<'a, I: T<'a>>(y: &'a Y) -> Box<dyn T<'a>> {
    return Box::new(I::new(y)) as Box<dyn T<'a>>;
}

then I got this error.

error[E0310]: the parameter type `I` may not live long enough

but parameter type I has constraint T<'a>, I don't figure out why this does not tell rustc to I::new(y) is alive after execution step go out from this function.

I suspect this is happened by I:new() returns Self, which has no lifetime annotation. but also I cannot find the way to give lifetime annotation to Self.

this is full example to cause the error.

struct Y {
    z: i32
}

struct X<'a> {
    y: &'a Y
}

trait T<'a> {
    fn new(y: &'a Y) -> Self where Self: Sized;
    fn do_something(&self);
}

impl<'a> T<'a> for X<'a> {
    fn new(y: &'a Y) -> X<'a> {
        return X::<'a> {
            y: y
        }
    }
    fn do_something(&self) {
        println!("{}", self.y.z)
    }
}

fn create_t_from_i<'a, I: T<'a>>(y: &'a Y) -> Box<dyn T<'a>> {
    // error: the parameter type `I` may not live long enough
    return Box::new(I::new(y)) as Box<dyn T<'a>>;
}

fn main() {
    let y = Y { z: 123 };
    {
        let t = create_t_from_i::<X>(&y);
        t.do_something()
    }
}

if I remove definition of T::new and giving X::new to create_t_from_i as function pointer, code seems work (need to use T + 'a instead of T<'a>). but what essential difference is from 1st example?

struct Y {
    z: i32
}

struct X<'a> {
    y: &'a Y
}

trait T {
    fn do_something(&self);
}

impl<'a> X<'a> {
    fn new(y: &'a Y) -> X<'a> {
        return X::<'a> {
            y: y
        }
    }
}
impl<'a> T for X<'a> {
    fn do_something(&self) {
        println!("{}", self.y.z)
    }
}

fn create_t_from_i<'a, I: T + 'a>(ctor: fn (y: &'a Y) -> I, y: &'a Y) -> Box<dyn T + 'a> {
    // if passing constructor function directly and using annotation I: T + 'a, rustc does not complain. 
    let i = ctor(y);
    return Box::new(i) as Box<dyn T + 'a>;
}

fn main() {
    let y = Y { z: 123 };
    {
        let t = create_t_from_i::<X>(X::new, &y);
        t.do_something()
    }
}

anyone have an idea why this happens and how to make `rustc' happy?

regards.

Upvotes: 0

Views: 169

Answers (2)

TheNappap
TheNappap

Reputation: 240

Any trait object has a lifetime annotations, which will implicitly be 'static. Your function is thus:

fn create_t_from_i<'a, I: T<'a>>(y: &'a Y) -> Box<dyn 'static + T<'a>> 

The solution is to explicitly annotate your trait object's lifetime:

fn create_t_from_i<'a, I: 'a + T<'a>>(y: &'a Y) -> Box<dyn 'a + T<'a>> 

Upvotes: 0

Freyja
Freyja

Reputation: 40884

fn create_t_from_i<'a, I: T<'a>>(y: &'a Y) -> Box<dyn T<'a>> 

is implicitly the same as

fn create_t_from_i<'a, I: T<'a>>(y: &'a Y) -> Box<dyn 'static + T<'a>> 

which in turns means that I must also have a static lifetime to be able to store it in the returned box (i.e. you need a I: 'static bound).

To solve this, you can add another lifetime parameter for the lifetime of I, and use that in the returned boxed trait. This will work no matter what the lifetime of I actually is:

fn create_t_from_i<'a, 'b, I: 'b + T<'a>>(y: &'a Y) -> Box<dyn 'b + T<'a>> 
//                     ^^^^^^^^^                               ^^^^

Playground example

Upvotes: 2

Related Questions