Ваш "уродливый взлом" на самом деле совершенно неверен и небезопасен.Вам не повезло, что Rust 1.32 не сообщает об ошибке, но, к счастью, Rust 1.34 делает.
Когда вы сохраняете в штучной упаковке значение, вы создаете тонкий указатель .Это занимает целочисленный для платформы размер целого числа (например, 32-разрядный в 32-разрядном x86, 64-разрядный в 64-разрядном x86 и т.объект, вы создаете толстый указатель .Он содержит тот же указатель на данные и ссылку на vtable .Этот указатель имеет размер два нативных целых числа:
+----------+----------+
| pointer | vtable |
| (0x1000) | (0xBEEF) |
+----------+----------+
При попытке выполнить преобразование объекта признака в ссылку вы теряете один из этих указателей, но это не определено, какой .Там нет никакой гарантии, что на первом месте: указатель данных или vtable.
Одно решение будет использовать std::raw::TraitObject
, но это нестабильно, потому что расположение жирных указателей все еще находится в воздухе.
Я бы порекомендовал решение, которое не требует кода unsafe
, это использование Any
:
use std::any::Any;
trait T: Any {}
struct X {}
impl T for X {}
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
let l = vec.last().unwrap();
Any::downcast_ref(l).unwrap()
}
Если вы не можете / не хотите использовать Any
, Мне сказали , что приведение указателя объекта черты к указателю на конкретный тип сохранит только указатель данных.К сожалению, я не могу найти официальную ссылку для этого, что означает, что я не могу полностью поручиться за этот код, хотя он работает эмпирически:
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
let last: &dyn T = &**vec.last().unwrap();
// I copied this code from Stack Overflow without reading
// it and it may not actually be safe.
unsafe {
let trait_obj_ptr = last as *const dyn T;
let value_ptr = trait_obj_ptr as *const X;
&*value_ptr
}
}
См. Также: