Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Rust 如何使用包含返回对Self的引用的方法的trait对象?_Rust - Fatal编程技术网

Rust 如何使用包含返回对Self的引用的方法的trait对象?

Rust 如何使用包含返回对Self的引用的方法的trait对象?,rust,Rust,使用包含返回对Self的引用的方法的trait对象的正确方法是什么?下面的代码 trait Foo{ fn gen(&mut self)->&self; fn评估(自我)->f64; } 结构A{ a:f64, } 求一个{ fn gen(&mut self)->&self{ 自我评价a=1.2; 自己 } fn评估(自我)->f64{ 自我评价a+2.3 } } 结构B; 对B的建议{ fn gen(&mut self)->&self{ 自己 } fn评估(自我)->f64{ 3.4 } }

使用包含返回对
Self
的引用的方法的trait对象的正确方法是什么?下面的代码

trait Foo{
fn gen(&mut self)->&self;
fn评估(自我)->f64;
}
结构A{
a:f64,
}
求一个{
fn gen(&mut self)->&self{
自我评价a=1.2;
自己
}
fn评估(自我)->f64{
自我评价a+2.3
}
}
结构B;
对B的建议{
fn gen(&mut self)->&self{
自己
}
fn评估(自我)->f64{
3.4
}
}
fn酒吧(f:&dyn Foo){
println!(“结果是:{}”,f.eval());
}
fn main(){
设mut-aa=A{A:0};
bar(aa.gen());
设mut-bb=B;
bar(bb.gen());
}
给出编译器错误

error[E0038]:无法将特征'Foo'生成对象
-->src/main.rs:30:1
|
3 | fn gen(&mut self)->&self;
|---方法“gen”在其参数或返回类型中引用“Self”类型
...
30 | fn酒吧(f:&dyn Foo){
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
现在,我们至少可以用两种方法中的一种来解决这个问题。我们可以将
gen
的定义修改为:

trait Foo{
fn gen(和mut self)->和self,其中self:Sized;
fn评估(自我)->f64;
}
或者,我们可以将bar的定义修改为:

fn条(f:&f),其中f:Foo+?大小{
println!(“结果是:{}”,f.eval());
}

也就是说,我不明白两者之间的区别,也不明白应该在什么情况下使用,或者是否应该使用另一种方法。

这里的关键是理解错误本身的原因。使用您的函数

fn bar(f : &dyn Foo) {
您可以调用
f.gen()
(给定
Foo的当前定义),但这不受支持,因为我们不知道它将返回什么类型!在特定代码的上下文中,它可以是
A
B
,在一般情况下,任何东西都可以实现该特性。这就是为什么

无法将trait
Foo
制成对象

如果它可以被做成一个trait对象,那么试图使用该对象的引用的代码就不会得到很好的定义,比如
f.gen()

现在,我们至少可以用两种方法中的一种来解决这个问题。我不明白这两种方法之间的区别,也不知道应该在什么情况下使用,或者是否应该使用另一种方法

  • fn gen(&mut self)->&self,其中self:size;

    这个函数,因为它现在对
    Self
    有一个限制,实际上不能被
    bar
    函数使用,因为
    dyn Foo
    没有
    size
    。如果你把这个限制放在适当的位置并尝试在
    bar
    内部调用
    f.gen()

    无法对trait对象调用
    gen
    方法

  • fn条(f:&f),其中f:Foo+?大小{

    这种方法解决了这个问题,因为我们确实知道
    f.gen()
    将返回什么类型(
    f
    )。还要注意,这可以简化为
    fn-bar(f:&f){
    ,甚至
    fn-bar(f:&impl-Foo){

  • 除非您真的在性能方面进行了超级优化,至少在某种程度上这是您的首选。您是希望传递一个trait对象,还是需要对对象传递到的每个函数执行

    更技术性的回答: 在技术方面,您可能不需要担心,这里的权衡是性能与可执行代码大小

    由于函数内部明确知道类型
    F
    ,因此您的通用
    bar
    函数实际上会在编译的输出可执行文件中创建
    bar
    函数的多个副本,就像您执行
    fn bar_A(F:&A){
    fn bar_B(F:&B)一样{
    。此过程称为
    monomorphization

    这个过程的好处是,因为函数有独立的副本,编译器可以更好地优化函数的代码,调用函数的位置也可以,因为
    F
    的类型是提前知道的
    bar\u A
    将始终调用
    A::eval
    bar\u B
    将始终调用
    B::eval
    ,当您调用
    bar(aa.gen());
    时,它已经知道它正在调用
    bar\u A(aa.gen())

    这里的缺点是,如果您有许多实现了
    Foo
    的类型,并且您为所有这些类型调用
    bar
    ,那么您将为这些类型创建同样多的
    bar\uxxx
    副本。这将使最终的可执行文件更大,但可能更快,因为编译器可以优化所有已知的类型把东西放在一起

    另一方面,如果使用
    fn-bar(f:&dyn-Foo){
    ,这两点可能会发生翻转。由于可执行文件中只有一个
    bar
    副本,因此在调用
    f.eval()时,它不知道
    f
    引用的类型
    ,这意味着您错过了潜在的编译器优化,您的函数需要这样做。
    f:&f
    知道类型
    f
    f:&dyn Foo
    需要查看与
    f
    相关的元数据,以确定要调用哪个trait实现的
    eval

    这一切都意味着对于
    f:&dyn Foo
    ,您的最终可执行文件将更小,这可能有利于RAM的使用,但如果将
    bar
    作为应用程序核心逻辑循环的一部分调用,那么它可能会更慢

    有关更多解释,请参阅