fn main() -> std::io::Result<()> {
let path = std::env::args().nth(1).expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
let writer;
if output_path.is_none() {
writer = std::io::stdout();
} else {
writer = std::fs::File::open(output_path.unwrap()).unwrap();
}
minidecaf::run(&input, &mut writer)
}
当未提供命令行参数时output_path应stdout()
writer
,在提供命令行参数时应File
output_path。 此代码不起作用,因为无法在编译时确定writer
的类型。
我把它改成下面。由于File
和Stdout
都实现了std::io::Write
,我需要的是对std::io::Write
的引用。由于函数run
的签名是:
pub fn run(input: &str, output: &mut impl std::io::Write) -> std::io::Result<()>
fn main() -> std::io::Result<()> {
let path = std::env::args().nth(1).expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
let mut writer : &mut std::io::Write;
if output_path.is_none() {
writer = &mut std::io::stdout();
} else {
writer = &mut std::fs::File::open(output_path.unwrap()).unwrap();
}
minidecaf::run(&input, &mut writer)
}
但它也不起作用。它有终身问题。
然后我把它改成:
fn main() -> std::io::Result<()> {
let path = std::env::args().nth(1).expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
minidecaf::run(&input, &mut (if output_path.is_none() {std::io::stdout()} else {std::fs::File::open(output_path.unwrap()).unwrap()}) )
}
它与第一段代码有相同的问题。
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:11:83
|
11 | minidecaf::run(&input, &mut (if output_path.is_none() {std::io::stdout()} else {std::fs::File::open(output_path.unwrap()).unwrap()}) )
| ----------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Stdout`, found struct `File`
| |
| expected because of this
那么,如何在未提供命令行参数时output_pathwriter
stdout()
,而在提供命令行参数时File
output_path呢?
正如你所指出的,你需要writer
成为对std::io::Write
的某种引用,但你也需要有人拥有相应的值。这里显而易见的解决方案是使用Box
:
fn run(input: &str, output: &mut impl std::io::Write) -> std::io::Result<()> {
unimplemented!();
}
fn main() -> std::io::Result<()> {
let path = std::env::args()
.nth(1)
.expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
let mut writer: Box<dyn std::io::Write>;
if output_path.is_none() {
writer = Box::new(std::io::stdout());
} else {
writer = Box::new(std::fs::File::open(output_path.unwrap()).unwrap());
}
run(&input, &mut writer)
}
操场
我想你可能想阅读更多关于如何使用特质的信息。
下面是一个示例代码,可以做你想要的:
#[derive(Default)]
struct A {
a: usize,
}
#[derive(Default)]
struct B {
b: usize,
}
trait C {
fn test(&self);
}
impl C for A {
fn test(&self) {
println!("A");
}
}
impl C for B {
fn test(&self) {
println!("B");
}
}
fn main() {
let t: Box<dyn C> = if true {
Box::new(A::default())
} else {
Box::new(B::default())
};
t.test();
}
你可以在这里尝试
为了避免动态调度的开销,可以使用either
crate 中的Either
类型来包装值,如果可能的话,它会自动实现标准库中的所有(大多数?)特征:
use {
either::Either,
std::{fs::File, io},
};
let mut writer = match output_path {
Some(path) => Either::Left(File::open(path)?),
None => Either::Right(io::stdout()),
};
(游乐场)
在这里,在我看来,使用match
表达在概念上更清晰。
在不使用外部板条箱的情况下,还可以使用变量模拟Either
枚举的行为:
use std::{env, fs::File, io};
let (mut stdout, mut file);
let writer: &mut dyn io::Write = match output_path {
Some(path) => {
file = File::open(path)?;
&mut file
}
None => {
stdout = io::stdout();
&mut stdout
}
};
(游乐场)