[英]How to put a reference to a trait object in an Option?
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. 我想在结构中的
Option
中存储对io::Write
特征对象的引用,但我无法弄清楚如何。 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: 然后从(例如)像这样的
BufWriter
分配它:
let logstream = &BufWriter::new(f);
let parameters = Parameters {
log: logstream, // Other elements removed
};
This works, but I'd like the logstream
to be optional. 这有效,但我希望
logstream
是可选的。 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. 你必须以某种方式明确地用特征类型键入
BufWriter
,并且你还必须更加小心一生。
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<&mut io::Write>
,一步完成,分三步完成:
Option<W>
-> Option<io::BufWriter<W>>
to make ow
. Option<W>
- > Option<io::BufWriter<W>>
来制作ow
。 This is the thing that will own the BufWriter
. 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. BufWriter
然后立即对其进行引用,那么该引用的生命周期将限定在模式匹配的分支内,因此借用检查器将会抱怨。 Option<io::BufWriter<W>>
to Option<&mut io::BufWriter<W>>
using as_mut()
. as_mut()
从Option<io::BufWriter<W>>
为Option<&mut io::BufWriter<W>>
。 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. into
,但似乎没有一个实现,用于转换特征的实现者和特征对象的引用之间的转换。 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
. 在这个例子中,
file()
返回一个Vec<u8>
作为底层Vec<u8>
,我这样做是为了使解决方案在Rust操场上可测试 ,但这也可以是一个File
。
Finally, I removed the lifetime constraint on the trait object, the type system/borrow checker didn't seem to think it was necessary. 最后,我删除了trait对象的生命周期约束,类型系统/借用检查器似乎认为没有必要。
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? 这是一个很好的解决方案还是上面建议的更好?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.