特质指针

4

当我开始学习Rust时,我天真地认为Rust指向特征的指针就像C++指向基类的指针一样实现,于是写了一些即使在这种假设下也能正常工作的代码。具体而言,我编写的代码与需要读取和搜索流的FFI库进行接口交互,大致如下:

struct StreamParts {
    reader: *mut Read,
    seeker: *mut Seek,
}

fn new_ffi_object<T: Read + Seek + 'static>(stream: T) -> FFIObject {
    let stream_ptr   = Box::into_raw(Box::new(stream));
    let stream_parts = Box::into_raw(Box::new(StreamParts {
        reader: stream_ptr as *mut Read,
        seeker: stream_ptr as *mut Seek,
    }));

    ffi_library::new_object(stream_parts, ffi_read, ffi_seek, ffi_close)
}

extern "C" fn ffi_read(stream_parts: *mut StreamParts, ...) -> c_ulong {
    (*stream_parts.reader).read(...)
    ...
}

extern "C" fn ffi_seek(stream_parts: *mut StreamParts, ...) -> c_ulong {
    (*stream_parts.seeker).seek(...)
    ...
}

extern "C" fn ffi_close(stream_parts: *mut StreamParts) {
    mem::drop(Box::from_raw(stream_parts.reader));
    mem::drop(Box::from_raw(stream_parts));
}

它是有效的。但是,有三件事我不完全明白为什么它有效:

  1. Rust的特质对象很胖,包含两个指针。因此,与C++不同,*mut Read是指向特质对象的指针,对吗?这个特质对象在哪里分配?Rust文档没有涉及到这个特定的情况。
  2. 我是否正确地认为mem::drop(Box::from_raw(stream_parts.reader))完全放弃了原始流?
  3. 为什么在new_ffi_object()中需要'static

有趣的问题;我知道 Rust 的引用很胖,但我不知道指针是否也是(我假设它们必须是?)。 - Matthieu M.
1
请分别提出问题:One question per question, please。想一想当你在寻找这些问题的答案时(你确实搜索过,对吧?),你会点击一个标题为“指向特征”的问题来回答这些要点吗?SO不是关于回答你的问题,而是建立一个世界级的知识库,供每个人在未来参考。我保证如果你分别提问,至少其中一个问题已经被回答了。 - Shepmaster
1个回答

5

指针和引用的行为完全相同,除了借用检查器禁止您拥有悬空引用以及您需要将指针解引用包装到unsafe块中。

  1. 是的,sizeof::<*mut Read>() == sizeof::<*mut ()>() * 2。trait对象并未在任何地方分配。它只是一个具有两个字段的结构体。一个指向数据的指针,一个指向虚表的指针。虚表在静态内存中分配。
  2. 正确。它访问reader的虚表指针,并查找虚表中的drop实现。
  3. 如果您没有'static 生命周期,则您的T可能包含比'static生命周期更短的引用。所有这些生命周期绑定意味着T没有此类引用,因此可以在没有限制的情况下在堆上复制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接