Как поместить ссылку на объект черты в опцию? - PullRequest
0 голосов
/ 20 апреля 2019

Я хотел бы сохранить ссылку на io::Write объект черты внутри Option в структуре, но я не могу понять, как. Я могу поставить ссылку прямо так:

pub struct Parameters<'a> {
    pub log: &'a (io::Write + 'a),
    // Other elements removed
}

, а затем назначьте его (например) из BufWriter следующим образом:

let logstream = &BufWriter::new(f);
let parameters = Parameters {
    log: logstream, // Other elements removed
};

Это работает, но я бы хотел, чтобы logstream был необязательным. Если я попытаюсь:

pub struct Parameters<'a> {
    pub log: Option<&'a(io::Write + 'a)>,
    // Other elements removed
}

и

let logstream = match f {
    Some(f) => Some(&BufWriter::new(f)),
    None => None,
};

let parameters = Parameters {
    log: logstream,
    // Other elements removed
};

Я получаю:

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>>>`

Какой подход здесь подходит?

Ответы [ 3 ]

0 голосов
/ 20 апреля 2019
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()
    );
}

Вместо того, чтобы взять Option<W> и попытаться преобразовать его в Option<&mut io::Write> за один раз, сделайте это в три шага:

  • Option<W> -> Option<io::BufWriter<W>>, чтобы сделать ow. Это вещь, которая будет владеть BufWriter. Как указано в другом ответе, если вы попытаетесь создать BufWriter в сопоставлении с образцом, а затем сразу же получите ссылку на него, время жизни этой ссылки будет ограничено в пределах ветви сопоставления с образцом, поэтому средство проверки заимствований будет жаловаться.
  • Следующим шагом является преобразование из Option<io::BufWriter<W>> в Option<&mut io::BufWriter<W>> с использованием as_mut().
  • Наконец, преобразовать ссылку на реализацию в объект универсального признака. В этом случае с картой, содержащей бросок. Я пытался использовать into, но, похоже, не существует реализации для преобразования между ссылкой на разработчик признака и ссылкой на объект признака.

В этом примере file() возвращает Vec<u8> в качестве основного автора, я сделал это так, чтобы решение было тестируемым на игровой площадке Rust , но это также может быть File .

Наконец, я удалил ограничение времени жизни для объекта черты, система типов / средство проверки заимствований, похоже, не считало это необходимым.

0 голосов
/ 20 апреля 2019

Спасибо за ответы выше. До того, как они были опубликованы, у меня была идея, и я придумал это (сокращенный до минимального примера, который показывает более или менее то, что я пытаюсь сделать):

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);
        });
    }
}

Это хорошее решение или один из предложенных выше будет лучше?

0 голосов
/ 20 апреля 2019

Вы должны как-то явно набрать BufWriter с типом черты, и вы также должны быть более осторожны с жизнями.

Вот эскиз того, как это может работать:

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());
}
...