Horse是一个实现Animal特性的结构。我有一个Rc<Horse>和一个需要接受Rc<Animal>的函数,所以我想从Rc<Horse>转换到Rc<Animal>。
我做了这个:
use std::rc::Rc;
struct Horse;
trait Animal {}
impl Animal for Horse {}
fn main() {
let horse = Rc::new(Horse);
let animal = unsafe {
// Consume the Rc<Horse>
let ptr = Rc::into_raw(horse);
// Now it's an Rc<Animal> pointing to the same data!
Rc::<Animal>::from_raw(ptr)
};
}这是个好办法吗?这是正确的吗?
发布于 2018-11-13 12:35:07
Boiethios的答复已经解释了可以使用as显式执行向上转换,甚至可以在某些情况下隐式执行。我想补充一些关于机制的更多细节。
我将从解释为什么您的不安全代码正确工作开始。
let animal = unsafe {
let ptr = Rc::into_raw(horse);
Rc::<Animal>::from_raw(ptr)
};unsafe块中的第一行使用horse并返回一个*const Horse,这是一个指向具体类型的指针。指针正是您所期望的--horse数据的内存地址(忽略了在您的示例中Horse为零大小且没有数据的事实)。
pub unsafe fn from_raw(ptr: *const T) -> Rc<T>因为我们要为Rc::<Animal>调用这个函数,所以预期的参数类型是*const Animal。然而,我们拥有的ptr有*const Horse类型,那么为什么编译器会接受代码呢?答案是编译器执行不大小的强制,这是一种隐式强制转换,即在某些地方为某些类型执行。具体来说,我们将指向具体类型的指针转换为实现Animal特性的任何类型的指针。因为我们不知道确切的类型,现在指针不再仅仅是一个内存地址了--它是一个内存地址和对象的实际类型的标识符,一个所谓的fat指针。这样,从fat指针创建的Rc可以保留底层具体类型的信息,并可以为Horse的Animal实现调用正确的方法(如果有的话;在您的示例中,Animal没有任何函数,但如果有,则应该继续工作)。
我们可以通过打印两种指针的大小来看出它们之间的区别。
let ptr = Rc::into_raw(horse);
println!("{}", std::mem::size_of_val(&ptr));
let ptr: *const Animal = ptr;
println!("{}", std::mem::size_of_val(&ptr));此代码首先使ptr成为一个*const Horse,然后打印指针的大小,然后使用不大小的强制将ptr转换为和*const Animal,然后再次打印其大小。在64位系统上,这将打印
8
16第一个只是一个简单的内存地址,而第二个是一个内存地址以及关于切入点的具体类型的信息。(具体来说,fat指针包含指向虚拟方法表的指针。)
现在让我们看看Boethios的答案中的代码发生了什么
let animal = horse as Rc<Animal>;或等量
let animal: Rc<Animal> = horse;也要执行不大小的强制。编译器如何知道如何对Rc而不是原始指针执行此操作?答案是专门为此目的而存在。您可以阅读用于动态大小类型的矫顽器上的RFC以获得更多详细信息。
发布于 2018-11-13 08:22:52
我认为您的解决方案是正确的,而我不是不安全代码的专家。但是,您不必使用不安全的代码来做简单的事情,比如向上转换:
use std::rc::Rc;
trait Animal {}
struct Horse;
impl Animal for Horse {}
fn main() {
let horse = Rc::new(Horse);
let animal = horse as Rc<Animal>;
}如果要将其传递给函数,甚至不需要强制转换:
fn gimme_an_animal(_animal: Rc<Animal>) {}
fn main() {
let horse = Rc::new(Horse);
gimme_an_animal(horse);
}因为Horse实现了Animal,所以马就是动物。你不必做任何特殊的事情来铸造它。请注意,这种转换是破坏性的,您不能从Rc<Horse>中生成Rc<Animal>。
https://stackoverflow.com/questions/53276642
复制相似问题