【问题标题】:Rust trait object's &self cannot be used in a trait default functionRust 特征对象的 &self 不能在特征默认函数中使用
【发布时间】:2016-10-05 21:49:39
【问题描述】:

试图覆盖 trait cast 问题,描述为 here。坚持实现特征函数,该函数返回具有自己实现的枚举实例:

//the "trait matcher" enum
enum Side<'a> {
    Good(&'a GoodDude),
    Bad(&'a BadDude),
}

//very general trait
trait Dude {
    fn who_am_i(&self) -> Side;
    fn do_useful_stuff(&self);
}

//specific trait #1
trait GoodDude: Dude {
    fn who_am_i_inner(&self) -> Side {
        Side::Good(&self)
    }

    fn save_the_world(&self);
}

//specific trait #2
trait BadDude: Dude {
    fn who_am_i_inner(&self) -> Side {
        Side::Bad(&self)
    }

    fn do_evil(&self);
}

但是由于某种原因,这部分的编译失败了E0277:

trait GoodDude: Dude {        
    fn who_am_i_inner(&self) -> Side {
        Side::Good(&self) //&self should be &GoodDude, but compiler says it is not...
    }

    fn save_the_world(&self);
}

结果:

<anon>:16:20: 16:25 error: the trait `GoodDude` is not implemented for the type `&Self` [E0277]
<anon>:16         Side::Good(&self)
                             ^~~~~
<anon>:16:20: 16:25 help: see the detailed explanation for E0277
<anon>:16:20: 16:25 note: required for the cast to the object type `GoodDude`

这可以解决吗?

完整示例:https://play.rust-lang.org/?gist=8ae2384e401da76c16214c4a642ce8b4&version=stable&backtrace=0

【问题讨论】:

  • 尝试Side::Good(self) 而不是Side::Good(&amp;self)。
  • 我不能。特征的实现者可能是一个巨大的结构,我必须在那里使用借用和特征对象。此外,它会立即询问 Size(特征 core::marker::Sized 未实现)
  • fn name(&amp;self) 可以脱糖为fn name(self:&amp;Self)。 self 变量的类型是 &amp;Self。

标签: oop casting rust traits


【解决方案1】:

首先fn who_am_i_inner中self的类型已经是一个引用,所以你不需要&amp;。

fn who_am_i_inner(&self) -> Side {
    Side::Good(self)
}

但随后 rustc 抱怨...

<anon>:13:20: 13:24 error: the trait `core::marker::Sized` is not implemented for the type `Self` [E0277]
<anon>:13         Side::Good(self)
                             ^~~~
<anon>:13:20: 13:24 help: see the detailed explanation for E0277
<anon>:13:20: 13:24 note: `Self` does not have a constant size known at compile-time
<anon>:13:20: 13:24 note: required for the cast to the object type `GoodDude`

诚然,错误消息非常不清楚,E0277 是关于完全不同的东西。让我们尝试使用 nightly 编译器,它会提供更好的错误消息:

error: the trait bound `Self: std::marker::Sized` is not satisfied [--explain E0277]
  --> <anon>:13:20
13 |>         Side::Good(self)
   |>                    ^^^^
help: consider adding a `where Self: std::marker::Sized` bound
note: required for the cast to the object type `GoodDude`

好的,让我们尝试添加where Self: Sized:

fn who_am_i_inner(&self) -> Side where Self: Sized {
    Side::Good(self)
}

现在它可以工作了。

世界拯救了。按任意键继续
愿 4 日与你同在
皮尤皮尤
卢克,我是你的父亲


where Self: Sized 是 Rust 表示 the method cannot be used from trait objects 的方式。如果您喜欢 C++,我们说该方法从“对象安全”中忽略,或者“不能是虚拟的”。

效果是,如果你得到的只是luke: &amp;GoodDude,那么你就不能调用luke.who_am_i_inner(),因为*luke 的大小未知。

我们需要使方法不是对象安全的原因是由于转换&amp;Self → &amp;GoodDude。在 Rust 中,像 &amp;GoodDude 这样的 trait 对象引用是一个 胖指针,在内部它表示为一个 2 元组 (pointer, method_table)。然而,在一个特征中,self 是一个细指针。

我们无法将瘦指针转换为胖指针,因为缺少信息,method_table。如果我们知道具体类型,则可以填写。这就是我们添加where Self: Sized 的原因。

如果您想让who_am_i_inner 对象安全,那么您不能提供默认实现。

【讨论】:

  • 谢谢。这明确描述了我需要什么。现在至少我可以将这种方法用作“特征转换”的一种形式。
  • 请参阅this idea。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-05
  • 1970-01-01
  • 2015-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多