Как реализовать черту со «статическим временем жизни для структуры с временем жизни» а? - PullRequest
2 голосов
/ 14 марта 2019

У меня есть trait Surface: 'static, который я хочу реализовать для struct Obj<'a>.Черта должна быть 'static, потому что я хочу хранить объекты типа Surface в Vec<Box<Surface>>.

На первом шаге я попробовал это.

impl<'a> Surface for Obj<'a> {}

Это будетне работает из-за несоответствия времени жизни между 'static и 'a.Другими словами: Surface может жить дольше, чем Obj, потому что Surface - это 'static.Я изменил свою реализацию следующим образом.

impl<'a> Surface for Obj<'a> where 'a: 'static {}

Насколько я правильно понимаю документацию, то, что я делаю, 'a может пережить 'static.Хочу ли я это?

Если я передам владение Obj<'a>, компилятор скажет мне, что изменяемая ссылка внутри Obj не будет жить достаточно долго и все еще будет заимствована.

Здесьэто короткий пример.

trait Surface: 'static {}

struct Manager {
    storage: Vec<Box<Surface>>,
}

impl Manager {
    fn add(&mut self, surface: impl Surface) {
        self.storage.push(Box::new(surface));
    }
}

struct SomeOtherStruct {}

struct Obj<'a> {
    data: &'a mut SomeOtherStruct,
}

impl<'a> Obj<'a> {
    fn new(some_struct: &'a mut SomeOtherStruct) -> Self {
        Obj { data: some_struct }
    }
}

impl<'a> Surface for Obj<'a> where 'a: 'static {}

fn main() {
    let mut some_struct = SomeOtherStruct {};
    let mut manager = Manager {
        storage: Vec::new(),
    };

    let obj = Obj::new(&mut some_struct);
    manager.add(obj);
}

( Детская площадка )

error[E0597]: `some_struct` does not live long enough
  --> src/main.rs:33:24
   |
33 |     let obj = Obj::new(&mut some_struct);
   |               ---------^^^^^^^^^^^^^^^^-
   |               |        |
   |               |        borrowed value does not live long enough
   |               argument requires that `some_struct` is borrowed for `'static`
34 |     manager.add(obj);
35 | }
   | - `some_struct` dropped here while still borrowed

Другими словами &mut some_struct - это время жизни 'a, но требуется 'static.Хорошо, это понятно, потому что some_struct живет в Obj<'a>, поэтому это не может быть 'static?

Это то, что я пытаюсь сделать "Rust like"?Я понятия не имею, как заставить это работать.Это действительно путает с жизнями.Я думаю, что могу обойти это, используя Rc<T>, но это усложнит ситуацию.

Ответы [ 3 ]

3 голосов
/ 14 марта 2019

Перво-наперво:

impl<'a> Surface for Obj<'a> where 'a: 'static {}

является многословным для

impl Surface for Obj<'static> {}

Вы правильно определили свою проблему:

Другими словами &mut some_struct - это время жизни 'a, но требуется 'static

Вам необходимо объявить some_struct как static:

fn main() {
    static mut SOME_STRUCT: SomeOtherStruct = SomeOtherStruct {};
    // ...
    let obj = unsafe { Obj::new(&mut SOME_STRUCT) };
    //  ...
}

Проблема в том, что вы не можете получить доступизменяемые статические переменные безопасно, потому что они могут быть преобразованы в несколько потоков одновременно, и это является проблемой, поэтому вам нужно unsafe.

Так что нет, ваш код не похож на Rust, но я боюсьВы не можете изменить это с вашей текущей архитектурой.


Эта черта должна быть статической, потому что я хочу хранить объекты типа Surface в Vec<Box<Surface>>.

Я не понимаю, почему вы думаете, что вам нужно 'static во-первых, например, этот код совершенно законен:

trait Foo {}
struct Bar;

impl Foo for Bar {}

fn main() {
    let b: Box<Foo> = Box::new(Bar);
}
2 голосов
/ 14 марта 2019

Как реализовать черту с 'static временем жизни для структуры с временем жизни 'a?

Вы не можете и не можете. Цель времени жизни 'static состоит в том, чтобы сказать «что-то, что живет в течение всей продолжительности программы». Никакое произвольное время жизни 'a не соответствует этому требованию , за исключением 'static.

1 голос
/ 14 марта 2019

@ hellow's answer работает и решает мою проблему, но чувствует себя хакером и работает против Rust.

С вашими подсказками я нашел лучшее решение, которое также работает и не использует unsafe.

Раствор 1

Я указал явные параметры времени жизни для Manager и для типа Box<Surface + 'a>:

trait Surface {}

struct Manager<'a> {
    storage: Vec<Box<Surface + 'a>>,
}

impl<'a> Manager<'a> {
    fn add(&mut self, surface: impl Surface + 'a) {
        self.storage.push(Box::new(surface));
    }
}

struct SomeOtherStruct {}

struct Obj<'a> {
    data: &'a mut SomeOtherStruct,
}

impl<'a> Obj<'a> {
    fn new(some_struct: &'a mut SomeOtherStruct) -> Self {
        Obj {
            data: some_struct
        }
    }
}

impl<'a> Surface for Obj<'a> {}

fn main() {
    let mut some_struct = SomeOtherStruct{};
    let mut manager = Manager { storage: Vec::new() };

    let obj = Obj::new(&mut some_struct);
    manager.add(obj);
}

( Детская площадка )

Решение 2

Сохраните Box<SomeOtherStruct> вместо &mut SomeOtherStruct в Obj. Это исключит время жизни:

trait Surface {}

struct Manager {
    storage: Vec<Box<Surface>>,
}

impl Manager {
    fn add(&mut self, surface: impl Surface + 'static) {
        self.storage.push(Box::new(surface));
    }
}

struct SomeOtherStruct {}

struct Obj {
    data: Box<SomeOtherStruct>,
}

impl Obj {
    fn new(some_struct: Box<SomeOtherStruct>) -> Self {
        Obj {
            data: some_struct
        }
    }
}

impl Surface for Obj {}

fn main() {
    let some_struct = SomeOtherStruct{};
    let mut manager = Manager { storage: Vec::new() };

    let obj = Obj::new(Box::new(some_struct));
    manager.add(obj);
}

( Детская площадка )

На мой взгляд, оба решения хороши. Я не знаю, какое решение лучше, и у меня нет опыта с преимуществами и недостатками этих решений. Для меня (возможно, потому что я новичок и все еще склоняюсь к Rust) легче избежать жизней и использовать Box, Rc и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...