Почему программа проверки заимствований жалуется на время жизни этих разных слайсов?

Почему в приведенном ниже коде использование среза массива работает, а использование среза 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, все будет работать нормально?
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Справочник 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.

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