Почему в приведенном ниже коде использование среза массива работает, а использование среза Vec
— нет?
use rand::{rngs::adapter::ReadRng, RngCore};
use std::io::Read;
struct MyRng {
rng: Box<dyn RngCore>,
}
pub fn main() {
// Version 1: error
//
let data = Vec::<u8>::new();
let data_slice = data.as_slice();
// Version 2: works
//
// let data_slice = &[0_u8][..];
// Version 3: error (!?!)
//
// let data = [0_u8];
// let data_slice = &data[..];
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
// With this commented out, all versions work.
MyRng { rng };
}
Есть несколько вещей, которые меня озадачивают:
data
отбрасывается при заимствовании, но указывает на конец области видимости — разве к тому времени не отбрасывается все?MyRng
, все будет работать нормально?Справочник Rust по Lifetime Elision:
Если черта не имеет границ времени жизни, то время жизни выводится в выражениях и находится
'static
вне выражений.
Таким образом, по умолчанию трейт-объекты в штучной упаковке получают привязку 'static
. Итак, эта структура:
struct MyRng {
rng: Box<dyn RngCore>,
}
На самом деле расширяется до этого:
struct MyRng {
rng: Box<dyn RngCore + 'static>,
}
Что заставляет вас создавать либо собственный тип, либо ссылку 'static
, чтобы выполнить ограничение. Однако вы можете полностью отказаться от неявной привязки 'static
, сделав свою структуру универсальной, после чего все разные версии кода скомпилируются:
use rand::{rngs::adapter::ReadRng, RngCore};
use std::io::Read;
struct MyRng<'a> {
rng: Box<dyn RngCore + 'a>,
}
pub fn main() {
// Version 1: now works
let data = Vec::<u8>::new();
let data_slice = data.as_slice();
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
let my_rng = MyRng { rng };
// Version 2: still works
let data_slice = &[0_u8][..];
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
let my_rng = MyRng { rng };
// Version 3: now works
let data = [0_u8];
let data_slice = &data[..];
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
let my_rng = MyRng { rng };
}
Чтобы ответить на ваши индивидуальные вопросы более прямо:
В чем разница между тремя подходами, если все они находятся в одной области?
Область действия и время жизни — это не одно и то же, но основное различие между тремя подходами заключается в том, что подход № 2 создает статический срез. Когда вы жестко кодируете некоторую &T
ссылку в свой код, не ссылаясь на какие-либо данные, принадлежащие какой-либо переменной, она записывается в сегмент двоичного файла только для чтения и получает 'static
время жизни.
Ошибка говорит о том, что данные удаляются при заимствовании, но указывает на конец области видимости — к тому времени не все отбрасывается?
Да, но ваш тип по определению требует, чтобы переданное значение было ограничено 'static
временем жизни, а поскольку подходы №1 и №3 не дают таких значений, компилятор отклоняет код.
Почему, если я удалю экземпляр
MyRng
, все будет работать нормально?
Поскольку в определении вашей структуры MyRng
нет ничего плохого, компилятор жалуется только тогда, когда вы пытаетесь создать ее неверный экземпляр.
[0_u8]
является константой, поэтому он подвергается статическому расширению rvalue, что позволяет ссылкам на него иметь статическое время жизни, поэтому он не будет удален. Ваш код не работает, потому что у вас есть фрагмент, который заимствует локальную переменную data
, но пытается создать из нее переменную MyRng
.