如何从特征对象中获取对具体类型的引用?
How to get a reference to a concrete type from a trait object?
如何从以下代码中的 a
变量中获取 Box<B>
或 &B
或 &Box<B>
:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
这段代码returns一个错误:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
在 Rust 中有两种向下转型的方法。第一种是使用 Any
。请注意,此 only 允许您向下转换为确切的原始具体类型。像这样:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
另一种方法是为基本特征(在本例中为 A
)的每个 "target" 实现一个方法,并为每个所需的目标类型实现转换。
等等,为什么我们需要as_any
?
即使您添加 Any
作为 A
的要求,它仍然无法正常工作。第一个问题是 Box<dyn A>
中的 A
将 也 实现 Any
... 这意味着当你调用 downcast_ref
时,你实际上将在对象类型 A
上调用它。 Any
只能 向下转换为调用它的类型,在本例中为 A
,因此您只能向下转换为 &dyn A
你已经拥有了。
但是某处 中的基础类型有一个Any
的实现,对吧?嗯,是的,但你不能得到它。 Rust 不允许你 "cross cast" 从 &dyn A
到 &dyn Any
.
那就是as_any
的作用;因为它只在我们的 "concrete" 类型上实现,所以编译器不会混淆它应该调用哪一个。在 &dyn A
上调用它会导致它动态分派到具体实现(同样,在本例中,B::as_any
),returns 和 &dyn Any
使用 [= 的实现12=] for B
, 这就是我们想要的。
请注意,您 可以 完全不使用 A
来回避整个问题。具体来说,以下 也 有效:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
但是,这会阻止您使用任何 other 方法; 所有你在这里可以做的是向下转换为具体类型。
作为潜在兴趣的最后一个说明,mopa crate 允许您将 Any
的功能与您自己的特征相结合。
应该清楚,如果有另一种类型 C
实现 A
并且您尝试将 Box<C>
转换为 Box<B>
,则转换可能会失败。我不知道你的情况,但对我来说,你似乎正在将其他语言的技术,比如 Java,引入 Rust。我在 Rust 中从未遇到过这种问题——也许你的代码设计可以改进以避免这种转换。
如果需要,您可以 "cast" 使用 mem::transmute
几乎任何东西。遗憾的是,如果我们只想将 Box<A>
转换为 Box<B>
或将 &A
转换为 &B
,我们就会遇到问题,因为指向 trait
的指针是一个胖子实际上由两个指针组成的指针:一个指向实际对象,一个指向 vptr。如果我们将它转换为 struct
类型,我们可以忽略 vptr。请记住,此解决方案非常不安全且非常老套——我不会在 "real" 代码中使用它。
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
编辑:见鬼去吧,它比我想象的还要不安全。如果你想以这种方式正确地做到这一点,你必须使用 std::raw::TraitObject
。不过这仍然不稳定。我认为这对 OP 没有任何用处;不要使用它!
在这个非常相似的问题中有更好的选择:How to match trait implementors
如何从以下代码中的 a
变量中获取 Box<B>
或 &B
或 &Box<B>
:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
这段代码returns一个错误:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
在 Rust 中有两种向下转型的方法。第一种是使用 Any
。请注意,此 only 允许您向下转换为确切的原始具体类型。像这样:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
另一种方法是为基本特征(在本例中为 A
)的每个 "target" 实现一个方法,并为每个所需的目标类型实现转换。
等等,为什么我们需要as_any
?
即使您添加 Any
作为 A
的要求,它仍然无法正常工作。第一个问题是 Box<dyn A>
中的 A
将 也 实现 Any
... 这意味着当你调用 downcast_ref
时,你实际上将在对象类型 A
上调用它。 Any
只能 向下转换为调用它的类型,在本例中为 A
,因此您只能向下转换为 &dyn A
你已经拥有了。
但是某处 中的基础类型有一个Any
的实现,对吧?嗯,是的,但你不能得到它。 Rust 不允许你 "cross cast" 从 &dyn A
到 &dyn Any
.
那就是as_any
的作用;因为它只在我们的 "concrete" 类型上实现,所以编译器不会混淆它应该调用哪一个。在 &dyn A
上调用它会导致它动态分派到具体实现(同样,在本例中,B::as_any
),returns 和 &dyn Any
使用 [= 的实现12=] for B
, 这就是我们想要的。
请注意,您 可以 完全不使用 A
来回避整个问题。具体来说,以下 也 有效:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
但是,这会阻止您使用任何 other 方法; 所有你在这里可以做的是向下转换为具体类型。
作为潜在兴趣的最后一个说明,mopa crate 允许您将 Any
的功能与您自己的特征相结合。
应该清楚,如果有另一种类型 C
实现 A
并且您尝试将 Box<C>
转换为 Box<B>
,则转换可能会失败。我不知道你的情况,但对我来说,你似乎正在将其他语言的技术,比如 Java,引入 Rust。我在 Rust 中从未遇到过这种问题——也许你的代码设计可以改进以避免这种转换。
如果需要,您可以 "cast" 使用 mem::transmute
几乎任何东西。遗憾的是,如果我们只想将 Box<A>
转换为 Box<B>
或将 &A
转换为 &B
,我们就会遇到问题,因为指向 trait
的指针是一个胖子实际上由两个指针组成的指针:一个指向实际对象,一个指向 vptr。如果我们将它转换为 struct
类型,我们可以忽略 vptr。请记住,此解决方案非常不安全且非常老套——我不会在 "real" 代码中使用它。
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
编辑:见鬼去吧,它比我想象的还要不安全。如果你想以这种方式正确地做到这一点,你必须使用 std::raw::TraitObject
。不过这仍然不稳定。我认为这对 OP 没有任何用处;不要使用它!
在这个非常相似的问题中有更好的选择:How to match trait implementors