2016-03-10 1 views
1

У меня есть что-то вроде следующего кода Rust, который может хранить объект Rust где-то (в реальном приложении он хранится в пользовательских данных Lua) и извлекать его позже (при вызове методов из Lua).Почему небезопасный код компилируется, но аналогичный код, который толкает вектор, жалуется, что ссылка не достаточно долгое время?

use std::ptr; 

struct Bar(u32); 

struct Foo<'a> { 
    subobj: &'a Bar, 
} 

struct State { 
    buf: [u8;100], 
} 

fn stash<T>(state: &mut State, foo: T) { 
    let p : *mut T = state.buf.as_ptr() as *mut T; 
    unsafe { ptr::write(p, foo); }; 
} 

fn fetch<T>(state: &mut State) -> &mut T { 
    let p : *mut T = state.buf.as_ptr() as *mut T; 
    unsafe { &mut *p } 
} 

fn main() { 
    let mut state = State{buf: [0;100]}; 
    // let mut v: Vec<Foo> = Vec::new(); 
    { 
     let bar = Bar(7); 
     let foo = Foo { subobj: &bar }; 

     // v.push(foo); // *does* complain that bar doesn't live long enough 
     stash(&mut state, foo); 
    } // bar's lifetime ends here! 
    let foo2: &mut Foo = fetch(&mut state); // Boom! 

    println!("{}", foo2.subobj.0 + 3); 
} 

В приведенном выше примере, очевидно, неправильно, так как это позволяет мне получить ссылку повисшего на bar после его сфера закончилась. Однако казалось бы, что любой тип, не содержащий ссылок (или только 'static), или что-то вроде Rc<T>.

Почему эта компиляция, но очень похожая программа (нажимать на вектор вместо этого), жалуется (по желанию), что ссылка на bar не проживает достаточно долго? Я не совсем понимаю, что отличает Vec::push.

Мое понимание заключается в том, что typechecking рассматривает только функции, а не функции. Для этих целей код unsafe не должен иметь значения; Дело в том, что я пытаюсь понять, как обернуть код unsafe в безопасный интерфейс.

+2

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

+1

100% согласны с A.B. Вот. Блок 'unsafe' сообщает компилятору:« Я буду защищать все гарантии безопасности памяти, потому что компилятор не может проверить их для меня », а затем вы идете и нарушаете все гарантии безопасности памяти. – Shepmaster

+1

Но код «небезопасный» скрыт внутри функций - у меня создалось впечатление, что проверка типа остановлена ​​у прототипа, а не заглядывает внутрь - и 'Vec' наверняка тоже небезопасный код под капотом. –

ответ

6

Но unsafe код скрыт внутри функций - Я был под впечатлением, что проверка типов остановился на прототипе, а не заглядывать внутрь - и Vec конечно, имеет небезопасный код под капотом тоже.

Вы правы, что проверка типа останавливается у прототипа. Разница здесь в том, что Vec включает в себя тип, который вы храните в своем собственном типе - это Vec<T>!

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

Для того, чтобы ваш код не в состоянии точно так же, как вектор, вы можете кодировать сохраненный тип с PhantomData:

use std::marker::PhantomData; 

struct State<T> { 
    buf: [u8; 100], 
    marker: PhantomData<T> 
} 

fn stash<T>(state: &mut State<T>, foo: T) { ... } 
fn fetch<T>(state: &mut State<T>) -> &mut T { ... } 

Теперь, когда вы stash ссылка на внутреннем блоке, тип State вызывается, чтобы иметь ссылку, и эта ссылка имеет срок службы. Тогда нормальная механика жизни не позволяет использовать ее вне блока.

Если вы хотите увидеть код в рабочем состоянии, обратите внимание, что вы должны двигаться let bar = Bar(7); до создания State, а также:

fn main() { 
    let bar = Bar(7); 

    let mut state = State { 
     buf: [0;100], 
     marker: PhantomData, 
    }; 

    let foo = Foo { subobj: &bar }; 
    stash(&mut state, foo); 

    let foo2: &mut Foo = fetch(&mut state); 

    println!("{}", foo2.subobj.0 + 3); 
} 

Я не буду говорить о том, что код, который я написал здесь, на самом деле безопасен - это требует больше мышления и проверки!

+0

Я буквально прочитал эту часть Rustonomicon этим утром в поезде, чтобы работать, и я изо всех сил пытался понять, где это может быть полезно за пределами примера в Vec. Спасибо за это! –

+0

Спасибо, я вернусь и снова прочитаю The Rustonomicon, теперь это более актуально! –