У меня есть структура, которую можно преобразовать в текст с помощью сложной серии вызовов методов, содержащих множество вызовов write!
. Этот текст можно записать в файл или в журнал отладки. Я пытаюсь решить, использовать ли fmt::Write
или io::Write
. Я не могу использовать оба, потому что тогда все методы записи должны быть дублированы.
Упрощенный пример:
impl MyType {
fn write_it(&self, writer: &mut impl ???) {
// ^ fmt::Write or io::Write?
self.write_header(writer);
self.write_contents(writer);
self.write_footer(writer);
}
fn write_header(&self, writer: &mut impl ???) {
write!(writer, "...")
}
// and more...
}
Документы для fmt::Write
скажем,
... Это похоже на свойство стандартной библиотеки io :: Write, но оно предназначено только для использования в libcore.
Итак, это наводит меня на мысль, что я должен использовать io::Write
. Это (очевидно) будет хорошо работать для таких типов ввода-вывода, как BufWriter
. Примечательно, что serde_ json делает это таким образом .
// if I use io::Write, I can do this
my_type.write_it(&mut BufWriter::new(File::create("my-file.txt")?)?;
Я также хочу использовать свой тип с format!
и подобными макросами. Поэтому мне нужно реализовать Display
. Фактически, не является ли Display
фактической чертой для типа, который может быть представлен как String
?
// I want to do this
println!("Contents:\n{}", my_type);
// or this
let mut s = String::new();
write!(s, "{}", my_type);
Так что я думаю, что я просто буду использовать ie в своей реализации. с Display
. Но вот проблема:
impl Display for MyType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_it(f)?;
// ^ the trait `std::io::Write` is not implemented for `std::fmt::Formatter<'_>`
}
}
Я могу исправить это, используя impl fmt::Write
, но тогда я теряю упомянутую выше возможность ввода-вывода. Между двумя типами нет переходника (я предполагаю, что он был бы, если бы это было предполагаемое использование). Так что я оказался между ними двумя. Какую черту мне следует использовать? Есть ли идиоматический c способ получить функциональность обоих?
Edit: Adapter?
@ ÖmerErden предложил создать адаптер, подобный следующему. Я просто не уверен, какие условия приведут к сбою преобразования UTF-8. Гарантированно ли это преобразование не потерпит неудачу, если я напишу действительный UTF-8 с write!
? Мне это кажется слишком рискованным.
struct Adapter<'a> {
f: &'a mut dyn fmt::Write,
}
impl<'a> io::Write for Adapter<'a> {
fn write(&mut self, b: &[u8]) -> Result<usize, io::Error> {
let s = str::from_utf8(b)
.map_err(|_| io::Error::from(io::ErrorKind::Other))?;
self.f.write_str(s)
.map_err(|_| io::Error::from(io::ErrorKind::Other))?;
Ok(b.len())
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}