Структура с полем признака, но необязательная

Скажем, у меня есть структура, реализация которой записывает где-то, то есть во что-то, что реализует черту std::io::Write. Однако я не хочу, чтобы структура владела этим. Следующий код работает:

fn main() {
    let mut out = std::io::stdout();
    let mut foo = Foo::new(&mut out);
    foo.print_number(2);
}

struct Foo<'a> {
    out: &'a mut dyn std::io::Write
}

impl<'a> Foo<'a> {
    pub fn new(out: &'a mut dyn std::io::Write) -> Self {
        Self {
            out
        }
    }
    
    pub fn print_number(&mut self, i: isize) {
        writeln!(self.out, "The number is {}", i).unwrap()
    }
}

Но теперь эту функцию записи следует сделать необязательной. Я думал, что это звучит достаточно просто, но теперь следующее не компилируется:

fn main() {
    let mut out = std::io::stdout();
    let mut foo = Foo::new(Some(&mut out));
    foo.print_number(2);
}

struct Foo<'a> {
    out: Option<&'a mut dyn std::io::Write>
}

impl<'a> Foo<'a> {
    pub fn new(out: Option<&'a mut dyn std::io::Write>) -> Self {
        Self {
            out
        }
    }
    
    pub fn print_number(&mut self, i: isize) {
        if self.out.is_some() {
            writeln!(self.out.unwrap(), "The number is {}", i).unwrap()
        }
    }
}

из-за:

error[E0507]: cannot move out of `self.out` which is behind a mutable reference
        --> src/main.rs:20:26
        |
        20 |                 writeln!(self.out.unwrap(), "The number is {}", i).unwrap()
        |                          ^^^^^^^^
        |                          |
        |                          move occurs because `self.out` has type `Option<&mut dyn std::io::Write>`, which does not implement the `Copy` trait
        |                          help: consider borrowing the `Option`'s content: `self.out.as_ref()`

что я не знаю, как интерпретировать.

Я попытался последовать предложению, изменив соответствующую строку на:

writeln!(self.out.as_ref().unwrap(), "The number is {}", i).unwrap()

но потом я получаю

error[E0596]: cannot borrow data in a `&` reference as mutable
--> src/main.rs:20:26
|
20 |                 writeln!(self.out.as_ref().unwrap(), "The number is {}", i).unwrap()
|                          ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable

Я действительно не уверен, как интерпретировать эти сообщения об ошибках, и, что удивительно, я ничего не добился, просто разбрасывая & и mut в случайных местах, не понимая ничего!

(Кстати, я не уверен, что это хороший способ сделать это в любом случае? Я открыт для совершенно разных подходов к решению этой проблемы, которые в основном заключаются в необязательной передаче чего-то для записи в структуру, но без структуры, владеющей им. Я читал о типе Box, который также может иметь значение?)


person cb7    schedule 05.01.2021    source источник
comment
Хотя это кажется основополагающим, is_some() обычно не то, что вам нужно. Обычно вы хотите знать, что Some и хотят получить доступ к внутреннему значению, а сопоставление с образцом (через match или if let) обеспечивает и то, и другое.   -  person kmdreko    schedule 05.01.2021


Ответы (1)


Как вы уже знаете, на основе того, что вы уже использовали &mut для out. Проблема с использованием as_ref() заключается в том, что он возвращает неизменяемую ссылку. Вместо этого вам нужно использовать as_mut().

pub fn print_number(&mut self, i: isize) {
    if self.out.is_some() {
        writeln!(self.out.as_mut().unwrap(), "The number is {}", i).unwrap()
    }
}

Кроме того, вы также можете упростить это и выразить более идиоматично, используя if let :

pub fn print_number(&mut self, i: isize) {
    if let Some(out) = &mut self.out {
        writeln!(out, "The number is {}", i).unwrap()
    }
}

Я бы также посоветовал вместо разворачивания вернуть _8 _ и позвольте вызывающей стороне обработать любую потенциальную ошибку.

pub fn print_number(&mut self, i: isize) -> std::io::Result<()> {
    if let Some(out) = &mut self.out {
        writeln!(out, "The number is {}", i)?;
    }
    Ok(())
}

Вы также можете упростить свои пути, например std::io::Write и std::io::Result<()>, импортировав их с помощью объявления использования, например use std::io::{self, Write};, а затем измените их на Write и io::Result<()>.

person vallentin    schedule 05.01.2021