I have some code that needs to create and use a quick_xml::Writer
with either a File
or Cursor
depending on user input. What is the proper Rust way (non-inheritance/no downcasting) to create a Writer
with different structs but same trait?
All the answers on stack overflow seem to be old and recommend allocating on the heap with Box, but this no longer works, and personally it feels wrong anyway.
Related but outdated now: How do I overcome match arms with incompatible types for structs implementing same trait?
Code:
use std::fs::File;
use std::io::Cursor;
use quick_xml::Writer;
fn main() {
let some_option = Some("some_file.txt");
let writer = match &some_option {
Some(file_name) => {
Writer::new(File::create(file_name).unwrap())
},
_ => {
Writer::new(Cursor::new(Vec::new()))
},
};
}
Error:
error[E0308]: `match` arms have incompatible types
--> src\main.rs:13:13
|
8 | let writer = match &some_option {
| __________________-
9 | | Some(file_name) => {
10 | | Writer::new(File::create(file_name).unwrap())
| | --------------------------------------------- this is found to be of type `Writer<File>`
11 | | },
12 | | _ => {
13 | | Writer::new(Cursor::new(Vec::new()))
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `File`, found struct `std::io::Cursor`
14 | | },
15 | | };
| |_____- `match` arms have incompatible types
|
= note: expected type `Writer<File>`
found struct `Writer<std::io::Cursor<Vec<u8>>>`
For more information about this error, try `rustc --explain E0308`.
Both File
and Cursor
implement std::io::Write
, so you can solve this by boxing those inner values, giving yourself a Writer<Box<Write>>
:
let writer: Writer<Box<dyn std::io::Write>> = Writer::new(match &some_option {
Some(file_name) => {
Box::new(File::create(file_name).unwrap())
},
_ => {
Box::new(Cursor::new(Vec::new()))
},
});
Note that this requires a heap allocation for the File
/Cursor
value. Alternatively, you can use the either
crate and its primary type Either
instead:
let writer = Writer::new(match &some_option {
Some(file_name) => {
Either::Left(File::create(file_name).unwrap())
},
_ => {
Either::Right(Cursor::new(Vec::new()))
},
});
This approach doesn't require any additional heap allocation or indirection. This works because Either
implements Write
when both the Left
and Right
variants do.