Почему Box<trait> имеет другой размер, чем Box<struct>?
Рассмотрим код:
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>
почему мы получаем эту ошибку?
2 ответа
В отличие от большинства языков с ОО-концепциями, в которые встроены виртуальные указатели class
, Rust использует подход с указателем жира, в котором он несет как виртуальный указатель, так и struct
указатель рядом друг с другом.
В результате макет Box<Trait>
для S
является:
+-------+-------+
| v-ptr | S-ptr |
+-------+-------+
который является 128-битным на 64-битной платформе.
Что касается уныния, на данный момент вы можете использовать Any
тип и его downcast_ref
а также downcast_mut
,
Вы также можете быть заинтересованы в ящике query_interface для более сложных вариантов использования.
Объект черты, такой как Box<Trait>
или же &Trait
, содержит два указателя:
- Указатель на данные
- Указатель на виртуальную таблицу
Два указателя (на 64-битных машинах) добавляют до 128 бит.
Box<Struct>
содержит только один указатель, непосредственно на данные. Никакой vtable не требуется, потому что определенные методы могут быть разрешены статически во время компиляции. Этот единственный указатель составляет всего 64 бита.
При условии
Box<Total>
действительноBox<S>
Они не. Если бы они были одинаковыми, почему у них были бы разные имена?:-)