我正在努力了解如何正确实现结构的任何类型的替代显示或特征。
\nstruct Fraction {\n numerator: u32,\n denominator: u32\n}\n\nimpl Fraction {\n fn unicode(&self) -> impl Display {\n // should I use named function or Closure?\n // Where does f come from?\n // can I re-use self?\n // How can I implement a trait that has multiple required functions or `type Output =`?\n fn fmt(&self, f: std::fmt::Formatter) -> std::fmt::Result {\n write!("{}\xe2\x81\x84{}", self.numerator, self.denominator)\n } // <- this returns `()`\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这不起作用,因为fn fmt- 作为函数定义 - 不会返回任何内容。使用未命名的闭包:
impl Fraction {\n fn unicode(&self) -> impl Display {\n // should I use named function or Closure?\n // Where does f come from?\n // can I re-use self?\n // How can I implement a trait that has multiple required functions or `type Output =`?\n |s: &Self, f: std::fmt::Formatter| -> std::fmt::Result {\n write!("{}\xe2\x81\x84{}", self.numerator, self.denominator)\n } // <- this returns `()`\n }\n}\n\n// somewhere else:\nimpl Display for Fraction {\n fn fmt(&self, f: std::fmt::Formatter) -> std::fmt::Result {\n write!("{}/{}", self.numerator, self.denominator)\n // ^-- ASCII \'/\'\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n说:
\nerror[E0277]: `{closure@src/fraction/generic_fraction.rs:422:9: 422:57}` doesn\'t implement `std::fmt::Display`\n --> src/fraction/generic_fraction.rs:418:39\n |\n418 | fn display_mixed<\'a>(&\'a self) -> impl \'a + fmt::Display \n | ^^^^^^^^^^^^^^^^^^^^^^ `{closure@src/fraction/generic_fraction.rs:422:9: 422:57}` cannot be formatted with the default formatter\n |\n = help: the trait `std::fmt::Display` is not implemented for closure `{closure@src/fraction/generic_fraction.rs:422:9: 422:57}`\n = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead\nRun Code Online (Sandbox Code Playgroud)\n类似问题:
\n\n与返回迭代器相反(我发现的大多数文档都是关于此的),我试图在功能块中实现我的特征。因此我找不到文档。也许这是错误的做法/思考方式?
\n正如 @cafce25 在评论中指出的那样,您必须返回实现该特征的东西,这在 Rust 中意味着您需要有一个实现该特征的类型的实例。但您仍然可以从签名和导出类型中“隐藏”这一点,例如通过在函数中声明类型:
\nfn foo() -> impl std::fmt::Display {\n struct Example;\n impl std::fmt::Display for Example {\n fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n write!(f, "hi")\n }\n }\n Example\n}\n\nfn main() {\n let d = foo();\n println!("{d}"); // prints "hi"\n}\nRun Code Online (Sandbox Code Playgroud)\n返回的实例必须携带足够的数据,以便其Display实现能够实际运行。在Example上面,没有数据(Example实际上是ZST),因为fmt实现只是将常量字符串写入格式化程序。
但更现实的是,您\xc2\xa0需要一些数据来实现,例如您的Fraction:
#[derive(Clone)]\nstruct Fraction {\n numerator: u32,\n denominator: u32\n}\n\nimpl Fraction {\n fn unicode(&self) -> impl std::fmt::Display {\n struct FractionAsUnicode(Fraction);\n impl std::fmt::Display for FractionAsUnicode {\n fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n write!(f, "{}\xe2\x81\x84{}", self.0.numerator, self.0.denominator)\n }\n }\n FractionAsUnicode(self.clone())\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n如果您想避免克隆并仅通过共享引用捕获原始部分(因为生成的对象只需要在打印调用期间存在),您可以这样做:
\nfn unicode(&self) -> impl std::fmt::Display + \'_ {\n struct FractionAsUnicode<\'a>(&\'a Fraction);\n impl<\'a> std::fmt::Display for FractionAsUnicode<\'a> {\n fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n write!(f, "{}\xe2\x81\x84{}", self.0.numerator, self.0.denominator)\n }\n }\n FractionAsUnicode(self)\n}\nRun Code Online (Sandbox Code Playgroud)\n在这里,我们声明了返回的“隐藏”对象的生命周期(即赋予函数\'a的原始引用的生命周期),我们也必须在块中拼写出来。这是带有引用的结构的标准。有点令人惊讶的是返回类型中添加了额外的内容。如果没有这个,编译器会抱怨:“捕获未出现在边界内的生命周期的隐藏类型”。也就是说,即使我们实际上没有在签名中说明生命周期,但返回实例的生命周期与原始实例的生命周期之间存在联系,即返回实例的生命周期不能比原始实例的生命周期长。Fractionunicodeimpl+ \'_impl std::fmt::DisplayunicodeFraction
对您在评论中提出的问题的简短回答:
\n-> impl Iterator<Item = i32>?由于您必须有一个完整的impl特征块,因此您可以像往常一样声明所有函数和关联的类型。self吗?不,如上所示,您需要捕获引用、克隆数据或将所需的一些数据提取到返回的实例中。最后,让我指出这-> impl SomeTrait并不意味着该函数返回一个特征对象(如-> Box<dyn SomeTrait>)。例如,该函数不能从不同分支返回不同的实现SomeTrait。该语法仅意味着存在一个实现该特征的类型,但您不会从签名中看到该类型。