为什么不安全的代码会编译,但是推送到向量的类似代码会抱怨引用的存活时间不够长?

Chr*_*son 1 unsafe lifetime rust

我有类似下面的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);
}
Run Code Online (Sandbox Code Playgroud)

上面的例子显然是错误的,因为它允许我bar在其范围结束后得到一个悬空引用.但是,对于任何不包含任何引用(或仅包含某些引用'static)或类似内容的类型,它似乎都可以Rc<T>.

为什么要编译,但是一个非常相似的程序(推送到矢量)抱怨(根据需要)引用bar不够长的存活?我真的不明白有什么不同Vec::push.

我的理解是,类型检查仅查看函数签名而不是函数体.出于这些目的,unsafe代码不应该是相关的; 关键是我正在试图弄清楚如何将unsafe代码包装到安全的界面中.

She*_*ter 6

unsafe代码隐藏在函数内部 - 我的印象是类型检查在原型处停止,而不是在内部偷看 - 并且Vec肯定还有不安全的代码.

你是正确的,类型检查在原型停止.这里的区别在于a 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 { ... }
Run Code Online (Sandbox Code Playgroud)

现在,当你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);
}
Run Code Online (Sandbox Code Playgroud)

我不会说我在这里写的代码实际上是安全的 - 这需要更多的思考和验证!