Generics 为什么可以';t`&;(?大小和特征)`被转换为`&;dyn特质`?

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都很简单。我想不出还有哪种情况是这样的。为

在下面的代码中,无法从对实现相同特征的动态大小类型的引用中获取对特征对象的引用。为什么会这样?如果我可以使用这两种方法来调用Trait方法,那么
&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指针,该对象只有两个指针(数据指针和
    Foo
    vtable指针)
  • 即使有两个指针,也不能将“胖指针”vtable与“瘦指针”vtable混合匹配,因为它们必须以不同的方式调用
因此,即使
dyn-Bar
实现
Foo
,也不可能将
&dyn-Bar
转换为
&dyn-Foo

尽管切片(另一种未大小化的类型)没有使用vtables实现,但指向它们的指针仍然很胖,因此同样的限制也适用于[i32]
的impl Foo

在某些情况下,您可以使用(仅在Rust 1.36的夜间)来表示边界,如“必须强制到
&dyn FooTrait
”。不幸的是,我不知道如何将此应用到您的案例中

另见
  • 有一个具体的示例,说明对未大小类型(
    str
    )的引用不能强制为对trait对象的引用

    • 不确定这是否解决了您的具体问题,但我确实用以下技巧解决了我的问题:

      我将以下方法添加到
      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");
          }
      }