Каков наилучший способ повторного использования заимствованного структурного поля, если оно не реализует свойство копирования или клонирования? - PullRequest
1 голос
/ 19 сентября 2019

Я пишу клиент для TCP-сервера

use std::net::TcpStream;
use std::io::{Read, Write, Error};

pub struct Client {
    addr: String,
    conn: Option<TcpStream>,
}

impl Client {
    pub fn connect(&mut self) {
        let res = TcpStream::connect(&self.addr);
        match res {
            Ok(c) => {
                self.conn = Some(c);
            },
            Err(_) => panic!(),
        }
    }

    pub fn version(self) -> Result<[u8; 8], Error> {
        let mut ver: [u8; 8] = [0;8];
        let command_string = b"VERSION\r\n";
        let res = self.conn.unwrap().write(&command_string[0..]);
        match res {
            Ok(_) => self.conn.unwrap().read(&mut ver[..]),
            Err(e) => panic!(e)
        };
        Ok(ver)
    }
}

Метод version возвращает ошибку

error[E0382]: use of moved value: `self.conn`

, когда я пытаюсь повторно использовать ссылку.Я думал об использовании #[derive(Copy, Clone)], но поскольку эти черты не реализованы для TCPStream, я не уверен, что делать.Каков наилучший способ добиться чтения и записи на сервер в методе (кстати, будет много таких методов, как версия, которая будет как читать, так и записывать на сервер)

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

1 Ответ

2 голосов
/ 20 сентября 2019

Метод version возвращает ошибку, когда я пытаюсь повторно использовать ссылку

self.conn не является ссылкой;это собственная стоимость.Поскольку Option::unwrap принимает его по значению (а его тип не реализует Copy), вы не можете повторно использовать его после вызова .unwrap();это может привести к небезопасности.

Но вы можете просто сохранить результат вызова unwrap в новой переменной и использовать вместо этого:

    pub fn version(self) -> io::Result<[u8; 8]> {
        let mut ver: [u8; 8] = [0;8];
        const COMMAND_STRING: &[u8] = b"VERSION\r\n";
        let mut conn = self.conn.unwrap();
        conn.write(COMMAND_STRING)?;
        conn.read(&mut ver[..])?;
        Ok(ver)
    }

Это возможно, потому что io::Write::writeи io::Read::read оба принимают &mut self, что означает, что никому не нужно принимать conn по значению.Вместо этого conn будет сброшено в конце version.См. Также Как предотвратить перемещение значения?

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

  • io::Result<_> isпсевдоним для Result<_, io::Error>, поэтому я изменил объявленный тип возвращаемого значения.Это распространенный шаблон во многих библиотеках, и я считаю, что эту форму легче понять.
  • conn.read() возвращает io::Result, который ранее игнорировался.Теперь conn.write() и conn.read() вызывают ошибки для вызывающего абонента с помощью специального оператора ?.Вы можете заменить ? на .unwrap(), если хотите, чтобы ошибки паниковали, а не распространялись.См. О чем этот оператор вопросительного знака?
  • COMMAND_STRING является const муравьем.Это спорно ли имеющий это имя вообще полезно по сравнению с только conn.write(b"VERSION\r\n"), но если вы будете иметь его, а const кажется, правильный путь.В принципе, компилятор может оптимизировать const s лучше, чем обычные переменные;на практике это вряд ли будет иметь большое значение в этом случае, но когда const более четко описывает использование этого значения, нет причин не делать этого.

Использование фактической ссылки

Если вы действительно хотите, чтобы version взял self по ссылке, и поэтому вы не хотите, чтобы он развернул и уничтожил self.conn, вы можете использовать Option::as_ref, чтобы сделать эторабота.

    pub fn version(&self) -> io::Result<[u8; 8]> {  // NOTE: `&self`
        let mut ver: [u8; 8] = [0;8];
        let mut conn = self.conn.as_ref().unwrap(); // NOTE: `.as_ref()`
        conn.write(b"VERSION\r\n")?;
        conn.read(&mut ver[..])?;
        Ok(ver)
    }

ссылка на игровую площадку

as_ref превращает &Option<_> в Option<&_>, так что unwrap пинг не потребляется self.conn но просто возвращает ссылку.См. Невозможно выйти из заимствованного содержимого при развертывании .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...