39

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.
John
  • 1,856
  • 2
  • 22
  • 33
  • I just created a ticket with Rust to improve the wording of the error message: https://github.com/rust-lang/rust/issues/103849 – Daniel Wolf Nov 01 '22 at 20:19

1 Answers1

45

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>)
}
Levans
  • 14,196
  • 3
  • 49
  • 53
  • Thanks a lot for the quick reply... But what is the use of a short-lived type? Can the compiler do any performance optimizations on it? – John Apr 20 '15 at 06:46
  • 1
    The use is actually then a type lifetime is tied to an other. For example an iterator's lifetime is tied to the lifetime of the object you iterate over, and this is reflected on its type. – Levans Apr 20 '15 at 06:59
  • @JohnFrancis: Another example of short-lived type: a reference to a variable in your function => the variable is short-lived (it will be destroyed at the end of the function) and therefore references to its value account for this (to prevent you from referencing this variable once it no longer is). – Matthieu M. Apr 20 '15 at 09:32
  • 1
    @MatthieuM. I know about the short-lived values/references... I was asking about a "data type definition" itself being short-lived? In the above example, T is a type, not a value/reference. – John Apr 20 '15 at 10:09
  • 2
    @JohnFrancis: Ah! The type itself is not short-lived (types exist or not), I would think this is a short-hand (!) for "types referencing short-lived variables". – Matthieu M. Apr 20 '15 at 11:43
  • 1
    @MatthieuM. Are you sure about that? Because I highly doubt that Rust developers would use a short-hand and make error messages confusing (they really care about error messages). Besides, writing `where T: Animal + 'static` does not mean that the argument `animal: T` should have static lifetime (I've checked that). And Levan's explanation makes more sense to me. – John Apr 20 '15 at 13:53
  • 2
    @JohnFrancis: I am afraid I did not explain myself clearly. I was attempting to paraphrase Levans' explanation. By type referencing short-lived variables I meant types that potentially contain a non `'static` reference to another type (such as `struct Foo<'a> { data: &'a str }`). – Matthieu M. Apr 20 '15 at 14:33
  • 2
    @MatthieuM. So... What `T: Animal + 'static` means is that if "`T` contains a reference, then it is having *at least* `'static` lifetime". Is that correct? Or is there another way to read it? – John Apr 20 '15 at 19:29
  • @JohnFrancis: You nailed it exactly! – Matthieu M. Apr 21 '15 at 06:07
  • I know this is an old post, but I'm still confused: what reference variable? I just don't see one in this example. Everything looks highly concrete. – Elf Sternberg May 08 '19 at 23:43
  • 3
    @ElfSternberg In the example provided, there's no use of references, but compiler also considers the case where `T` could potentially be a type of objects holding references that are short-lived. You could consider `ShortLivedBee` in the above example to be defined as `struct ShortLivedBee<'a> { myref: &'a int }` and an object of it holding a reference to an int living in a stack frame. This cannot be passed to `new_rc_animal` as I defined it in the question, because there the return type is implicitly `Rc>`. – John Jun 26 '19 at 08:08