使用 Vec<u8> 模拟文件 Write

Using a Vec<u8> to mock file Write

我的函数签名是:

fn write_header(source_pkey: PublicKey, target_pkey: PublicKey, nonce: Nonce,
    mut output: &mut Box<&mut dyn Write>) -> anyhow::Result<()> {

我正在尝试使用 Vec<u8> 模拟文件来测试它:

#[test]
fn test_write_header() {
    let mut vec = Vec::<u8>::new();
    let mut out = Box::new(&mut vec);
    write_header(
        PublicKey([1u8; 32]),
        PublicKey([2u8; 32]),
        Nonce([3u8; 24]),
        &mut out
    ).unwrap();
    assert_eq!(out.len(), 104);
}

但我收到错误消息:

error[E0308]: mismatched types
  --> src/encrypt.rs:45:13
   |
45 |             &mut out
   |             ^^^^^^^^ expected trait object `dyn std::io::Write`, found struct `Vec`
   |
   = note: expected mutable reference `&mut Box<&mut dyn std::io::Write>`
              found mutable reference `&mut Box<&mut Vec<u8>>

我正在使用 dyn std::io::Write,因为我需要此功能在正常操作中接受 FileStdout,以及 Vec<u8> 进行测试。

我是不是做错了,还是要让编译器相信 Vec<u8> 具有 Write 特性?


更新:

Box 是因为 Sized 问题,如果没有它,这段代码将无法编译。

/// Open the program's output file, or stdout if there is no input file.
/// Note: stdout on Windows only accepts utf8.
pub fn open_output(output: Option<String>) -> anyhow::Result<Box<dyn Write>> {
    if let Some(filename) = output {
        Ok(Box::new(File::open(&filename)
            .context(format!("unable to open '{filename}' for output"))?))
    } else {
        Ok(Box::new(stdout()))
    }
}

有没有办法删除这个 Box? (对后续问题深表歉意。)

解决方案是将 Box 保持在顶层,因为 main() 不知道 output 是否是 StdoutFile或其他。

我已将 write_header 的签名更改为:

fn write_header(source_pkey: PublicKey, target_pkey: PublicKey, nonce: Nonce,
    output: &mut dyn Write) -> anyhow::Result<()> {

根据 Fracis Gagné 的建议,解决了原始问题。

根据@FrancisGagne 的评论,您可以直接使用 &mut dyn Write,然后传递 &mut vec:

use anyhow; // 1.0.52
use std::io::Write;


fn write_header(
    source_pkey: (),
    target_pkey: (),
    nonce: (),
    mut output: &mut dyn Write,
) -> anyhow::Result<()> {
    output.write(&[1])?;
    anyhow::Ok(())
}

#[test]
fn test_write_header() {
    let mut vec = Vec::<u8>::new();
    write_header(
        (),
        (),
        (),
        &mut vec
    ).unwrap();
    assert_eq!(vec.len(), 1);
}

Playground

也可以使用impl Write作为输入参数。这很好,因为您实际上可以使您的函数适用于实现 Write 本身的任何东西:

fn write_header(
    source_pkey: (),
    target_pkey: (),
    nonce: (),
    mut output: impl Write,
) -> anyhow::Result<()> {
    output.write(&[1])?;
    anyhow::Ok(())
}

Playground