Почему тип параметра должен быть «статическим» при использовании трейта

Я играл с деревьями и чертами и столкнулся с поведением, которое не понял. Вот минимальный (не компилируемый) пример:

trait Trait<T> {}
struct Struct<T> {
    option: Option<Box<dyn Trait<T>>>, // change this to Option<Box<TestStruct<T>>>
                                       // and it works without issues
}
impl<T> Trait<T> for Struct<T> {}

fn set<T>(s: &mut Struct<T>) {                         // works when changed to "fn set <T: 'static> ..."
    s.option = Some(Box::new(Struct { option: None })) // "error[E0310]: the parameter type `T` may not live long enough"
}

Так что этот код работает либо с T: 'static, либо с Option<Box<TestStruct<T>>>, но не так, как есть, и я не смог найти удовлетворительного объяснения, почему.

Может кто-нибудь объяснить, что случилось с чертами и сроками жизни? Есть ли другой способ сделать это?

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
0
133
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Всякий раз, когда мы используем трейт-объект, здесь dyn Trait<T>, мы отказываемся от большого количества информации, которая обычно есть в Rust о характеристиках типа.

В частности, значения типа могут содержать ссылки, и в этом случае этот тип имеет одно или несколько жизненных циклов, которые определяют, как долго эти ссылки действительны. (Самый простой случай, когда тип сам является ссылкой; &'a str не может существовать дольше, чем время окончания, указанное 'a.

Но трейт-объект не определяет конкретный тип; это может быть любой тип, реализующий черту. Это означает, что трейт-объекты всегда должны учитывать, какое время жизни может содержать конкретный тип. Это делается с помощью «срока жизни» трейт-объекта. У каждого трейт-объекта есть срок жизни — он просто часто не записывается благодаря пожизненной элизии.

В частности, правила исключения говорят о том, что каждый раз, когда вы пишете Box<dyn Trait>, это эквивалентно Box<dyn Trait + 'static>. То есть любой тип X, который вы хотите превратить в этот типаж-объект, должен соответствовать границе X: 'static, что означает, что если он содержит какие-либо времена жизни, они должны пережить 'static — что в конкретном случае времени жизни 'static совпадает с говоря, что они должны быть равны 'static, потому что ни одна жизнь не длиннее 'static.

(Другой распространенный случай ограничения времени жизни трейт-объекта по умолчанию — это &'a dyn Trait, который согласно правилам эквивалентен &'a (dyn Trait + 'a) — разрешению всего, что пережило или равно времени жизни ссылки.)

Итак, объясняя то, что вы заметили:

  • Добавление T: 'static работает, потому что ваш TestStruct<T> (определение которого вы не дали, как я предполагаю) не содержит ссылок, поэтому T: 'static логически подразумевает TestStruct<T>: 'static, что удовлетворяет ограничению времени жизни типаж-объекта.

  • Использование Option<Box<TestStruct<T>>> вместо Option<Box<dyn Trait<T>>> означает, что здесь нет задействованного трейт-объекта, поэтому нет привязки 'static по умолчанию, поэтому Struct<T> является полностью общим — ему все равно, есть ли у T время жизни или нет.

В большинстве случаев, когда вы хотите использовать Box<dyn Trait> — для хранения в какой-либо структуре данных — привязка T: 'static является правильным выбором, потому что вещи, которые вы собираетесь хранить на потом, обычно не должны быть привязаны к определенному сроку службы — жить вечно. Но если вам нужно было допустить типы с нестатическим временем жизни, вы всегда можете вместо этого написать Box<dyn Trait + 'a> (при условии, что время жизни 'a объявлено).

Другие вопросы по теме