Generics 为什么可以';t`&;(?大小和特征)`被转换为`&;dyn特质`?
在下面的代码中,无法从对实现相同特征的动态大小类型的引用中获取对特征对象的引用。为什么会这样?如果我可以使用这两种方法来调用Trait方法,那么Generics 为什么可以';t`&;(?大小和特征)`被转换为`&;dyn特质`?,generics,rust,polymorphism,trait-objects,Generics,Rust,Polymorphism,Trait Objects,在下面的代码中,无法从对实现相同特征的动态大小类型的引用中获取对特征对象的引用。为什么会这样?如果我可以使用这两种方法来调用Trait方法,那么&dyn Trait和&(?size+Trait)之间的区别到底是什么 实现FooTraitContainerTrait的类型可能例如具有type Contained=dyn FooTrait或type Contained=T,其中T是实现FooTrait的具体类型。在这两种情况下,获得&dyn FooTrait都很简单。我想不出还有哪种情况是这样的。为
&dyn Trait
和&(?size+Trait)
之间的区别到底是什么
实现FooTraitContainerTrait
的类型可能例如具有type Contained=dyn FooTrait
或type Contained=T
,其中T
是实现FooTrait
的具体类型。在这两种情况下,获得&dyn FooTrait
都很简单。我想不出还有哪种情况是这样的。为什么在FooTraitContainerTrait
的一般情况下不可能这样做
trait FooTrait{
fn foo(和self)->f64;
}
///
性状鞋带内含物性状{
包含的类型:?尺寸+英尺;
fn get_ref(&self)->&self::Contained;
}
///
fn foo_dyn(dyn_some_foo:&dyn FooTrait)->f64{
dyn_some_foo.foo()
}
fn foo_generic(部分foo:&T)->f64{
一些_foo.foo()
}
///
fn foo_on_容器(包含_a_foo:&C)->f64{
让some_foo=包含_a_foo.get_ref();
//以下行不起作用:
//富丁(一些富丁)
//以下线路工程:
//一些_foo.foo()
//正如这一点:
foo_generic(一些foo)
}
取消注释foo\u-dyn(some\u-foo)
行会导致编译器错误
error[E0277]:编译时无法知道`::Contained`类型的值的大小
-->src/main.rs:27:22
|
27 |福丁(含)
|^^^^^^^^在编译时没有已知的大小
|
=help:trait`std::marker::Sized`未为包含的`::`
=注意:要了解更多信息,请访问
=帮助:考虑添加一个`WH::包含:STD:::标记::
=注意:强制转换为对象类型'dyn FooTrait'时需要`
此问题可以简化为以下简单示例(感谢):
如果此impl
有vtable,则它必须接受fat指针,因为impl
可能使用Bar
方法,这些方法在self
上动态调度
这导致两个问题:
- 在
对象中没有地方存储&dyn Foo
条
vtable指针,该对象只有两个指针(数据指针和
vtable指针)Foo
- 即使有两个指针,也不能将“胖指针”vtable与“瘦指针”vtable混合匹配,因为它们必须以不同的方式调用
dyn-Bar
实现Foo
,也不可能将&dyn-Bar
转换为&dyn-Foo
尽管切片(另一种未大小化的类型)没有使用vtables实现,但指向它们的指针仍然很胖,因此同样的限制也适用于[i32]的impl Foo
在某些情况下,您可以使用(仅在Rust 1.36的夜间)来表示边界,如“必须强制到&dyn FooTrait
”。不幸的是,我不知道如何将此应用到您的案例中
另见
- 有一个具体的示例,说明对未大小类型(
)的引用不能强制为对trait对象的引用str
- 不确定这是否解决了您的具体问题,但我确实用以下技巧解决了我的问题:
我将以下方法添加到
FooTrait
:
fn as_dyn(&self) -> &dyn FooTrait;
无法提供默认的impl(因为它要求Self
大小size
,但将FooTrait
限制为size
禁止为其创建特征对象…)
然而,对于所有的大小的实现,它的实现非常简单
fn as_dyn(&self) -> &dyn FooTrait { self }
因此,它基本上限制了FooTrait
的所有实现的大小,除了dyn FooTrait
引用自此,这很好地解释了fat指针
感谢您将问题简化为:
trait Foo{}
fn make_dyn(arg:&T)->&dyn Foo{
arg
}
这就带来了如何在不同大小的之间进行转换的问题
为了回答这个问题,让我们首先来看看无大小类型Trait
的实现
特征条{
fn bar_方法(和自身){
println!(“这是酒吧”);
}
}
酒吧{
fn foo_方法(和self){
println!(“这是foo”);
}
}
u8{}的impl条
u8{}的impl Foo
fn main(){
设x:u8=35;
设foo:&dynfoo=&x;
//我能做什么
//let bar:&dyn bar=foo;
}
那么,你能做什么呢代码>
//下面是所有的伪代码
pub struct TraitObjectFoo{
数据:*mut(),
vtable\u ptr:&VTableFoo,
}
发布结构VTableFoo{
布局:布局,
//析构函数
放置位置:不安全fn(*mut()),
//方法以确定性顺序显示
foo_方法:fn(*mut()),
bar_方法:fn(*mut()),
}
//字段包含u8实现的Foo和Bar方法地址
_U8的静态VTABLE_FOO_:VTableFoo=VTableFoo{…};
从伪代码中,我们可以知道
//让foo:&dyn foo=&x;
设foo=TraitObjectFoo{&x,&VTABLE_foo_FOR_U8};
//let bar:&dyn bar=foo;
//C++语法结构
设bar=traitbjectbar(traitbjectfoo{&x,&VTABLE_FOO_用于_U8});
bar
类型为traitbjectbar
,而不是traitbjectfoo
类型。也就是说,不能将一种类型的结构分配给另一种不同类型(在RIST中,C++中可以使用RealType CAST)。
你可以做些什么来获得另一个间接层次:
dyn Foo的执行条{
...
}
let bar:&dyn bar=&foo;
//TraitObjectFoo{&foo,&VTABLE_foo_-foo_-DYN_-foo}
同样的道理也适用于切片
铸造的工作环境
fn as_dyn(&self) -> &dyn FooTrait;
fn as_dyn(&self) -> &dyn FooTrait { self }
// blanket impl for all sized types, this allows for a very large majority of use-cases
impl<T: Bar> AsBar for T {
fn as_bar(&self) -> &dyn Bar { self }
}
// a helper-trait to do the conversion
trait AsBar {
fn as_bar(&self) -> &dyn Bar;
}
// note that Bar requires `AsBar`, this is what allows you to call `as_bar`
// from a trait object of something that requires `Bar` as a super-trait
trait Bar: AsBar {
fn bar_method(&self) {
println!("this is bar");
}
}
// no change here
trait Foo: Bar {
fn foo_method(&self) {
println!("this is foo");
}
}