簡體   English   中英

如何在選項中引用特征對象?

[英]How to put a reference to a trait object in an Option?

我想在結構中的Option中存儲對io::Write特征對象的引用,但我無法弄清楚如何。 我可以像這樣直接放入引用:

pub struct Parameters<'a> {
    pub log: &'a (io::Write + 'a),
    // Other elements removed
}

然后從(例如)像這樣的BufWriter分配它:

let logstream = &BufWriter::new(f);
let parameters = Parameters {
    log: logstream, // Other elements removed
};

這有效,但我希望logstream是可選的。 如果我嘗試:

pub struct Parameters<'a> {
    pub log: Option<&'a(io::Write + 'a)>,
    // Other elements removed
}

let logstream = match f {
    Some(f) => Some(&BufWriter::new(f)),
    None => None,
};

let parameters = Parameters {
    log: logstream,
    // Other elements removed
};

我明白了:

error[E0308]: mismatched types
  --> src/main.rs:17:14
   |
17 |         log: logstream,
   |              ^^^^^^^^^ expected trait std::io::Write, found struct `std::io::BufWriter`
   |
   = note: expected type `std::option::Option<&dyn std::io::Write>`
              found type `std::option::Option<&std::io::BufWriter<std::vec::Vec<u8>>>`

這里有什么合適的方法?

你必須以某種方式明確地用特征類型鍵入BufWriter ,並且你還必須更加小心一生。

這是一個如何工作的草圖:

use std::io;
use std::io::BufWriter;
use std::str::from_utf8;

pub struct Parameters<'a> {
    pub log: Option<&'a mut io::Write>,
    // Other elements removed
}

fn main() {
    let mut w = BufWriter::new(Vec::new());
    let rw: &mut std::io::Write = &mut w;
    let logstream = Some(rw);

    let parameters = Parameters {
        log: logstream,
        // Other elements removed
    };
    parameters.log.unwrap().write(b"hello world").ok();

    println!("{}", from_utf8(&w.into_inner().unwrap()).unwrap());
}
use std::io;
use std::io::BufWriter;

pub struct P<'a> {
    pub log: Option<&'a mut io::Write>,
}

fn file() -> Option<Vec<u8>> {
    Some(Vec::new())
}

fn main() {
    let mut ow = file().map(|f| BufWriter::new(f));
    let p = P {
        log: ow.as_mut().map(|w| w as &mut io::Write),
    };

    p.log.unwrap().write(b"Hi!").ok();

    println!(
        "{}",
        String::from_utf8(ow.unwrap().into_inner().unwrap()).unwrap()
    );
}

而不是Option<W>並嘗試將其轉換為Option<&mut io::Write> ,一步完成,分三步完成:

  • Option<W> - > Option<io::BufWriter<W>>來制作ow 這將是擁有BufWriter的東西。 正如在另一個答案中所指出的,如果您嘗試在模式匹配中創建BufWriter然后立即對其進行引用,那么該引用的生命周期將限定在模式匹配的分支內,因此借用檢查器將會抱怨。
  • 下一步是使用as_mut()Option<io::BufWriter<W>>Option<&mut io::BufWriter<W>>
  • 最后從引用轉換為實現,再轉換為通用trait對象。 在這種情況下,地圖包含一個演員表。 我曾嘗試使用into ,但似乎沒有一個實現,用於轉換特征的實現者和特征對象的引用之間的轉換。

在這個例子中, file()返回一個Vec<u8>作為底層Vec<u8> ,我這樣做是為了使解決方案在Rust操場上可測試 ,但這也可以是一個File

最后,我刪除了trait對象的生命周期約束,類型系統/借用檢查器似乎認為沒有必要。

感謝上面的答案。 在他們被發布之前,我自己有了一個想法並想出了這個(縮減到一個最小的例子,或多或少地顯示我正在嘗試做的事情):

use std::{env, process};
use std::fs::File;
use std::io::{self, BufWriter};

pub struct Parameters {
    pub log: Option<Box<dyn io::Write>>,
}


fn main() {
    let args: Vec<String> = env::args().collect();
    let logfile = if args.len() > 1 {
        Some(&args[1])
    } else {
        None
    };

    let logstream: Option<Box<dyn io::Write>> = match logfile {
        Some(logfile) => {
            let f = File::create(logfile).unwrap_or_else(|err| {
                eprintln!("ERROR opening logfile: {}", err);
                process::exit(1);
            });
            let logstream = BufWriter::new(f);
            Some(Box::new(logstream))
        }
        None => {
            None
        },
    };

    let parameters = Parameters {
        log: logstream,
    };
    play(parameters);
}

fn play(parameters: Parameters) {
    // Rest of code removed
    if let Some(mut stream) = parameters.log {
        stream.write("Testing\n".as_bytes()).unwrap_or_else(|err| {
            eprintln!("ERROR writing to logfile: {}", err);
            process::exit(1);
        });
    }
}

這是一個很好的解決方案還是上面建議的更好?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM