【问题标题】:Creating a struct with a generic trait for field. Expected struct<trait> found struct<type that implements said trait>为字段创建具有通用特征的结构。预期的 struct<trait> 找到了 struct<type that implements said trait>
【发布时间】:2015-11-19 19:27:25
【问题描述】:

我正在尝试创建一个具有 BufWriter 的结构,该结构使用 Write 特征,以便该结构可以具有可以是任何实现该特征的缓冲写入器:FileStream等。但是我的函数中存在一个问题,它创建的结构说我有mismatched types。这是一个具有相同问题的示例代码。

use std::fs::File;
use std::io::{BufWriter, Write};

pub struct BufWriterStruct<W: Write> {
    pub writer: Option<BufWriter<W>>,
}

impl <W: Write>BufWriterStruct<W> {
    pub fn new(filename: &str) -> BufWriterStruct<W> {
        BufWriterStruct {
            writer: Some(BufWriter::new(File::create(filename).unwrap())),
        }
    }
}

fn main() {
    let tmp = BufWriterStruct::new("tmp.txt");
}

Playground

有错误

error: mismatched types:
 expected `BufWriterStruct<W>`,
    found `BufWriterStruct<std::fs::File>`

如果我改为更改我的 new 函数,改为采用实现 Write 特征的参数并在创建 BufWriter 时使用它,它可以正常工作。

我觉得前者应该可以做到。

【问题讨论】:

  • 为什么你认为第一种方法应该是可能的? BufWriterStruct&lt;W&gt; 返回值意味着调用者可以选择函数返回的内容,但这显然不是您的方法中发生的情况。
  • 在我的实际情况中,我传递了另一个参数来告诉它要创建什么类型。就像它会传入一个字符串,如流、文件等,而新的会创建它。我只是想让 new 做所有事情(创建文件/流,做它需要做的任何事情,等等)。不过,我可以将文件/流的创建移到 new 之外。但越想越觉得自己想要的方式是不可能的。

标签: rust


【解决方案1】:

您的错误在于混合通用和特定:

impl <W: Write>BufWriterStruct<W> {
    pub fn new(filename: &str) -> BufWriterStruct<W> {
        BufWriterStruct {
            writer: Some(BufWriter::new(File::create(filename).unwrap())),
        }
    }
}

在这里,您的BufWriter 实例应该接受W: Write,这是由调用者决定的,但该函数实际上创建了一个File

让调用者决定,而不是:

impl <W: Write> BufWriterStruct<W> {
    pub fn new(writer: W) -> BufWriterStruct<W> {
        BufWriterStruct {
            writer: Some(BufWriter::new(writer)),
        }
    }
}

当然,这会稍微改变调用:

fn main() {
    let tmp = BufWriterStruct::new(File::create("tmp.txt").unwrap());
}

然后是it'll work

【讨论】:

    【解决方案2】:
    impl <W: Write>BufWriterStruct<W> {
        pub fn new(filename: &str) -> BufWriterStruct<W>
    

    这个签名意味着下面的代码是有效的:

    let tmp : BufWriterStruct<Stdout> = BufWriterStruct::new("tmp.txt");
    

    但是,这显然不适用于您的new 实现,因为它会生成BufWriterStruct&lt;File&gt;,而不是&lt;StdOut&gt;。如果你想返回一个BufWriterStruct&lt;File&gt;,你应该相应地声明你的new函数:

    pub fn new(filename: &str) -> BufWriterStruct<File>
    

    但是,仅此更改将使impl 块上的W 参数不受约束,并且编译器将无法为其推断类型。最好的解决方案是将new 方法放在非泛型impl 上:

    impl BufWriterStruct<File> {
        pub fn new(filename: &str) -> BufWriterStruct<File> {
            // ...
        }
    }
    

    请注意,Rust 不支持重载(具有相同名称但参数列表不同的方法),因此如果您在同一类型上有两个 impl 块(不考虑泛型参数),每个块都有一个名为 new 的方法,尝试调用其中一个时会出现错误(从 Rust 1.4.0 开始,仅在单独的 impl 块中定义具有相同名称的方法不是编译时错误)。因此,您可能希望使用比new 更明确的名称。

    【讨论】:

    • 是的。我认为他们按照我想做的方式是不可能的。我想我只是要将它更改为我传递实现Write 特征的参数的位置。然后我可以在FileStream等上使用它。
    猜你喜欢
    • 2022-11-20
    • 2021-10-01
    • 1970-01-01
    • 2022-10-06
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多