在 Rust 中写入文件或字符串

Pet*_*lka 6 rust

TL;DR:我想实现std::io::Write输出到内存缓冲区的特征,最好是字符串,用于单元测试目的。

我一定错过了一些简单的东西。

与另一个问题类似,在 Rust 中写入文件或标准输出,我正在编写可以与任何std::io::Write实现一起使用的代码。

它在如下定义的结构上运行:

pub struct MyStructure {
    writer: Box<dyn Write>,
}
Run Code Online (Sandbox Code Playgroud)

现在,很容易创建实例写入文件或stdout

impl MyStructure {
    pub fn use_stdout() -> Self {
        let writer = Box::new(std::io::stdout());
        MyStructure { writer }
    }

    pub fn use_file<P: AsRef<Path>>(path: P) -> Result<Self> {
        let writer = Box::new(File::create(path)?);
        Ok(MyStructure { writer })
    }
    
    pub fn printit(&mut self) -> Result<()> {
        self.writer.write(b"hello")?;
        Ok(())
    }
}
Run Code Online (Sandbox Code Playgroud)

但是对于单元测试,我还需要有一种方法来运行业务逻辑(这里用 method 表示printit())并捕获其输出,以便在测试中可以检查其内容。

我无法弄清楚如何实现这一点。这个操场代码显示了我想如何使用它,但它没有编译,因为它违反了借用规则。

// invalid code - does not compile!
fn main() {
    let mut buf = Vec::new(); // This buffer should receive output
    let mut x2 = MyStructure { writer: Box::new(buf) };
    x2.printit().unwrap();
    
    // now, get the collected output
    let output = std::str::from_utf8(buf.as_slice()).unwrap().to_string();
    
    // here I want to analyze the output, for instance in unit-test asserts
    println!("Output to string was {}", output);
}
Run Code Online (Sandbox Code Playgroud)

知道如何正确编写代码吗?即,如何在之后可以访问的内存结构(String,Vec,...)之上实现编写器?

Vla*_*eev 6

像这样的东西确实有效

let mut buf = Vec::new();

{
   // Use the buffer by a mutable reference
   //
   // Also, we're doing it inside another scope
   // to help the borrow checker

   let mut x2 = MyStructure { writer: Box::new(&mut buf) };
   x2.printit().unwrap();
}

let output = std::str::from_utf8(buf.as_slice()).unwrap().to_string();
println!("Output to string was {}", output);
Run Code Online (Sandbox Code Playgroud)

但是,为了使其工作,您需要修改您的类型并添加一个生命周期参数:

pub struct MyStructure<'a> {
    writer: Box<dyn Write + 'a>,
}
Run Code Online (Sandbox Code Playgroud)

请注意,在您的情况下(您省略了+ 'a部分),编译器假定您将其'static用作 trait 对象的生命周期:

// Same as your original variant
pub struct MyStructure {
    writer: Box<dyn Write + 'static>
}
Run Code Online (Sandbox Code Playgroud)

这限制了可以在这里使用的类型集,特别是,您不能使用任何类型的借用引用。因此,为了获得最大的通用性,我们必须在这里明确并定义生命周期参数。

另请注意,根据您的用例,您可以使用泛型而不是特征对象:

pub struct MyStructure<W: Write> {
    writer: W
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,类型在程序的任何点都是完全可见的,因此不需要额外的生命周期注释。

  • 如果您使用通用方法,如果遵循OP的方法,在创建“x2”期间将“buf”的所有权移至“x2”,则还可以通过“x2.writer”访问“buf”:[Playground](https: //play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=b8577f10c5fad034b4e7e2b87165931b) (2认同)