I'd like to store a reference to an io::Write
trait object inside an Option
in a struct but I can't figure out how. I can put the reference in directly like this:
pub struct Parameters<'a> {
pub log: &'a (io::Write + 'a),
// Other elements removed
}
and then assign it from (for example) a BufWriter
like this:
let logstream = &BufWriter::new(f);
let parameters = Parameters {
log: logstream, // Other elements removed
};
This works, but I'd like the logstream
to be optional. If I try:
pub struct Parameters<'a> {
pub log: Option<&'a(io::Write + 'a)>,
// Other elements removed
}
and
let logstream = match f {
Some(f) => Some(&BufWriter::new(f)),
None => None,
};
let parameters = Parameters {
log: logstream,
// Other elements removed
};
I get:
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>>>`
What is a suitable approach here?
You have to somehow explicitly type the BufWriter
with the trait type, and you also have to be more careful with lifetimes.
Here is a sketch of how this could work:
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()
);
}
Rather than taking an Option<W>
and try and convert it into an Option<&mut io::Write>
in one go, do it in three steps:
Option<W>
-> Option<io::BufWriter<W>>
to make ow
. This is the thing that will own the BufWriter
. As pointed out in the other answer, if you try and create a BufWriter
within the pattern match and then immediately take a reference to it, that reference's lifetime is scoped within the branch of the pattern match, so the borrow checker will complain. Option<io::BufWriter<W>>
to Option<&mut io::BufWriter<W>>
using as_mut()
. into
, but there doesn't seem to be an implementation for convert between a reference to the implementor of a trait and a reference to the trait object. In this example, file()
returns a Vec<u8>
as the underlying writer, I did this so that the solution was testable in a Rust playground , but this could as well be a File
.
Finally, I removed the lifetime constraint on the trait object, the type system/borrow checker didn't seem to think it was necessary.
Thanks for the answers above. Prior to them being posted I had an idea myself and came up with this (cut down to a minimal example that shows more or less what I'm trying to do):
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);
});
}
}
Is this a good solution or would one of the ones suggested above be better?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.