为什么Box<trait>的大小与Box<struct>不同?

7

考虑下面的代码:

use std::boxed::Box;
use std::mem::transmute;

trait Total {
    fn total(&self) -> i32;
}

#[derive(Debug)]
struct S {
    a: i32,
    b: i32,
    c: i32,
}

impl S {
    fn new() -> S {
        S { a: 2, b: 3, c: 4 }
    }
}

impl Total for S {
    fn total(&self) -> i32 {
        self.a + self.b + self.c
    }
}

fn main() {
    let b: Box<Total> = Box::new(S::new());
    unsafe {
        let s: Box<S> = std::mem::transmute(b);
        println!("S = {:?}", s);
    }
}

这会导致错误:

error[E0512]: transmute called with differently sized types: Box<Total> (128 bits) to Box<S> (64 bits)
  --> src/main.rs:30:29
   |
30 |             let s: Box<S> = std::mem::transmute(b);
   |                             ^^^^^^^^^^^^^^^^^^^

考虑到 Box<Total> 实际上是一个 Box<S>,为什么我们会收到这个错误提示?


你是不是想要 let b: Box<Total> - Aurora0001
我已经删除了第二个显然不太重要的问题 - Shepmaster
2个回答

10

不同于大部分使用在class中嵌入虚拟指针的面向对象编程语言,Rust采用了“fat-pointer”方法,在其中同时保存虚拟指针和struct指针。

因此,对于S类型而言,Box<Trait>的布局为:

+-------+-------+
| v-ptr | S-ptr |
+-------+-------+

在 64 位平台上,它为 128 位。


至于下转型(downcasting),目前您可以使用 Any 类型及其 downcast_refdowncast_mut 方法。

如果您有更复杂的用例,您可能还需要考虑使用 query_interface crate。


6

一个trait对象,比如Box<Trait>&Trait包含两个指针

  • 一个指向数据的指针
  • 一个指向vtable的指针

两个指针(在64位机器上)加起来总共有128位。

Box<Struct>只包含一个指向数据的指针。不需要vtable,因为特定的方法能够在编译时静态解析。这个单一指针只有64位。

考虑到Box<Total>实际上是一个Box<S>

它们并不相同。如果它们是相同的,为什么会有不同的名称?:-)


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