Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter type may not live long enough?

Tags:

rust

The following code segment gives me an error:

use std::rc::Rc;

// Definition of Cat, Dog, and Animal (see the last code block)
// ...

type RcAnimal = Rc<Box<Animal>>;
fn new_rc_animal<T>(animal: T) -> RcAnimal
where
    T: Animal /* + 'static */ // works fine if uncommented
{
    Rc::new(Box::new(animal) as Box<Animal>)
}

fn main() {
    let dog: RcAnimal = new_rc_animal(Dog);
    let cat: RcAnimal = new_rc_animal(Cat);
    let mut v: Vec<RcAnimal> = Vec::new();
    v.push(cat.clone());
    v.push(dog.clone());
    for animal in v.iter() {
        println!("{}", (**animal).make_sound());
    }
}
error[E0310]: the parameter type `T` may not live long enough
 --> src/main.rs:8:13
  |
4 | fn new_rc_animal<T>(animal: T) -> RcAnimal
  |                  - help: consider adding an explicit lifetime bound `T: 'static`...
...
8 |     Rc::new(Box::new(animal) as Box<Animal>)
  |             ^^^^^^^^^^^^^^^^
  |
note: ...so that the type `T` will meet its required lifetime bounds
 --> src/main.rs:8:13
  |
8 |     Rc::new(Box::new(animal) as Box<Animal>)
  |             ^^^^^^^^^^^^^^^^

but this compiles fine:

use std::rc::Rc;

// Definition of Cat, Dog, and Animal (see the last code block)
// ...

fn new_rc_animal<T>(animal: T) -> Rc<Box<T>>
where
    T: Animal,
{
    Rc::new(Box::new(animal))
}

fn main() {
    let dog = new_rc_animal(Dog);
    let cat = new_rc_animal(Cat);
}

What is the cause of the error? The only real difference seems to be the use of operator as. How can a type not live long enough? (playground)

// Definition of Cat, Dog, and Animal
trait Animal {
    fn make_sound(&self) -> String;
}

struct Cat;
impl Animal for Cat {
    fn make_sound(&self) -> String {
        "meow".to_string()
    }
}

struct Dog;
impl Animal for Dog {
    fn make_sound(&self) -> String {
        "woof".to_string()
    }
}

Addendum

Just to clarify, I had two questions:

  1. Why doesn't this work? ... which is addressed in the accepted answer.
  2. How can a type, as opposed to a value or reference, be shortlived? ... which was addressed in the comments. Spoiler: a type simply exists since it's a compile-time concept.
like image 532
John Avatar asked Apr 20 '15 06:04

John


1 Answers

There are actually plenty of types that can "not live long enough": all the ones that have a lifetime parameter.

If I were to introduce this type:

struct ShortLivedBee<'a>;
impl<'a> Animal for ShortLivedBee<'a> {}

ShortLivedBee is not valid for any lifetime, but only the ones that are valid for 'a as well.

So in your case with the bound

where T: Animal + 'static

the only ShortLivedBee I could feed into your function is ShortLivedBee<'static>.

What causes this is that when creating a Box<Animal>, you are creating a trait object, which need to have an associated lifetime. If you do not specify it, it defaults to 'static. So the type you defined is actually:

type RcAnimal = Rc<Box<Animal + 'static>>;

That's why your function require that a 'static bound is added to T: It is not possible to store a ShortLivedBee<'a> in a Box<Animal + 'static> unless 'a = 'static.


An other approach would be to add a lifetime annotation to your RcAnimal, like this:

type RcAnimal<'a> = Rc<Box<Animal + 'a>>;

And change your function to explicit the lifetime relations:

fn new_rc_animal<'a, T>(animal: T) -> RcAnimal<'a>
        where T: Animal + 'a { 
    Rc::new(Box::new(animal) as Box<Animal>)
}
like image 159
Levans Avatar answered Nov 01 '22 14:11

Levans