如何从函数返回“impl Display”

Fee*_*Fee 0 io traits rust

我正在努力了解如何正确实现结构的任何类型的替代显示或特征。

\n
struct 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}\n
Run Code Online (Sandbox Code Playgroud)\n

这不起作用,因为fn fmt- 作为函数定义 - 不会返回任何内容。使用未命名的闭包:

\n
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}\n
Run Code Online (Sandbox Code Playgroud)\n

说:

\n
error[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\n
Run Code Online (Sandbox Code Playgroud)\n

类似问题:

\n\n

与返回迭代器相反(我发现的大多数文档都是关于此的),我试图在功能块中实现我的特征。因此我找不到文档。也许这是错误的做法/思考方式?

\n

Aur*_*ílý 5

正如 @cafce25 在评论中指出的那样,您必须返回实现该特征的东西,这在 Rust 中意味着您需要有一个实现该特征的类型的实例。但您仍然可以从签名和导出类型中“隐藏”这一点,例如通过在函数中声明类型:

\n
fn 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}\n
Run Code Online (Sandbox Code Playgroud)\n

返回的实例必须携带足够的数据,以便其Display实现能够实际运行。在Example上面,没有数据(Example实际上是ZST),因为fmt实现只是将常量字符串写入格式化程序。

\n

但更现实的是,您\xc2\xa0需要一些数据来实现,例如您的Fraction

\n
#[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}\n
Run Code Online (Sandbox Code Playgroud)\n

如果您想避免克隆并仅通过共享引用捕获原始部分(因为生成的对象只需要在打印调用期间存在),您可以这样做:

\n
fn 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}\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,我们声明了返回的“隐藏”对象的生命周期(即赋予函数\'a的原始引用的生命周期),我们也必须在块中拼写出来。这是带有引用的结构的标准。有点令人惊讶的是返回类型中添加了额外的内容。如果没有这个,编译器会抱怨:“捕获未出现在边界内的生命周期的隐藏类型”。也就是说,即使我们实际上没有在签名中说明生命周期,但返回实例的生命周期与原始实例的生命周期之间存在联系,即返回实例的生命周期不能比原始实例的生命周期长。Fractionunicodeimpl+ \'_impl std::fmt::DisplayunicodeFraction

\n

对您在评论中提出的问题的简短回答:

\n
    \n
  • 多个函数或关联类型怎么样,例如-> impl Iterator<Item = i32>?由于您必须有一个完整的impl特征块,因此您可以像往常一样声明所有函数和关联的类型。
  • \n
  • 我可以重复使用self吗?不,如上所示,您需要捕获引用、克隆数据或将所需的一些数据提取到返回的实例中。
  • \n
\n

最后,让我指出这-> impl SomeTrait并不意味着该函数返回一个特征对象(如-> Box<dyn SomeTrait>)。例如,该函数不能从不同分支返回不同的实现SomeTrait。该语法仅意味着存在一个实现该特征的类型,但您不会从签名中看到该类型。

\n