Тип параметра может не долго жить?

Следующий фрагмент кода выдает ошибку:

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>)
  |             ^^^^^^^^^^^^^^^^

но это компилируется нормально:

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);
}

В чем причина ошибки? Единственное реальное отличие, похоже, заключается в использовании оператора as. Как тип может жить недостаточно долго? (площадка)

// 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()
    }
}

Ответ 1

На самом деле существует множество типов, которые могут "не жить достаточно долго": все те, у которых есть параметр продолжительности жизни.

Если бы я представил этот тип:

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

ShortLivedBee недействителен для любого времени жизни, но только те, которые действительны для 'a.

Итак, в вашем случае с привязкой

where T: Animal + 'static

единственный ShortLivedBee, который я мог бы использовать в вашей функции, - ShortLivedBee<'static>.

Причиной этого является то, что при создании Box<Animal> вы создаете объект-объект, который должен иметь связанное с ним время жизни. Если вы не укажете его, по умолчанию будет 'static. Таким образом, тип, который вы определили, фактически:

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

Чтобы ваша функция требовала добавления 'static в T: Невозможно сохранить ShortLivedBee<'a> в Box<Animal + 'static>, если только 'a = 'static.


Другим подходом было бы добавить аннотацию на всю жизнь к вашему RcAnimal, например:

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

И измените вашу функцию на явное отношение жизни:

fn new_rc_animal<'a, T>(animal: T) -> RcAnimal<'a>
        where T: Animal + 'a { 
    Rc::new(Box::new(animal) as Box<Animal>)
}