Ржавчина: Как исправить заимствованную стоимость, которая не живет достаточно долго

#rust

Вопрос:

У меня есть простое клиентско-серверное приложение. Я получаю сообщение на стороне сервера от клиента, но я хочу отправить этот ответ по каналу с сервера в другой файл, и я получаю ошибку «заимствованное значение живет недостаточно долго».

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

На данный момент, если кто-нибудь может помочь мне исправить этот код (возможно, отредактировать ту часть кода, которую необходимо исправить), это было бы полезно.

Заранее спасибо.

На стороне сервера:

 use std::os::unix::net::UnixDatagram;
use std::path::Path;

fn unlink_socket (path: impl AsRef<Path>) {
    let path = path.as_ref();
    if Path::new(path).exists() {
        let result = std::fs::remove_file(path);
        match result {
            Err(e) => {
                println!("Couldn't remove the file: {:?}", e);
            },
            _ => {}
        }
    }
}

pub fn tcp_datagram_server() {
    pub static FILE_PATH: amp;'static str = "/tmp/datagram.sock";
    let (tx, rx) = mpsc::channel();
    let mut buf = vec![0; 1024];
    unlink_socket(FILE_PATH);
    let socket = match UnixDatagram::bind(FILE_PATH) {
        Ok(socket) => socket,
        Err(e) => {
            println!("Couldn't bind: {:?}", e);
            return;
        }
    };
    println!("Waiting for client to connect...");
    loop {
        let received_bytes = socket.recv(buf.as_mut_slice()).expect("recv function failed");
        println!("Received {:?}", received_bytes);
        let received_message = from_utf8(buf.as_slice()).expect("utf-8 convert failed");
        tx.clone().send(received_message);
    }
}

fn main() {
   tcp_datagram_server();
}
 

сторона клиента:

 use std::sync::mpsc;
use std::os::unix::net::UnixDatagram;
use std::path::Path;
use std::io::prelude::*;

pub fn tcp_datagram_client() {
    pub static FILE_PATH: amp;'static str = "/tmp/datagram.sock";
    let socket = UnixDatagram::unbound().unwrap();
    match socket.connect(FILE_PATH) {
        Ok(socket) => socket,
        Err(e) => {
            println!("Couldn't connect: {:?}", e);
            return;
        }
    };
    println!("TCP client Connected to TCP Server {:?}", socket);
    loop {
        socket.send(b"Hello from client to server").expect("recv function failed");
    }
}

fn main() {
   tcp_datagram_client();
}
 

Ошибка, которую я получаю

 error[E0597]: `buf` does not live long enough
  --> src/unix_datagram_server.rs:38:42
   |
38 |         let received_message = from_utf8(buf.as_slice()).expect("utf-8 convert failed");
   |                                          ^^^ borrowed value does not live long enough
...
41 | }
   | -
   | |
   | `buf` dropped here while still borrowed
   | borrow might be used here, when `tx` is dropped and runs the `Drop` code for type `std::sync::mpsc::Sender`
   |
   = note: values in a scope are dropped in the opposite order they are defined

error: aborting due to previous error; 8 warnings emitted
 

Ответ №1:

На данный момент, если кто-нибудь может помочь мне исправить этот код (возможно, отредактировать ту часть кода, которую необходимо исправить), это было бы полезно.

Что ж, послание кажется довольно ясным. send делает именно то, что он говорит, он отправляет параметр по каналу. Это означает, что данные должны жить достаточно долго и оставаться действительными «навсегда» (они должны быть живыми и действительными в канале, а также при извлечении из него получателем).

Здесь это не так. rustc не может понять, что функция никогда не возвращается, и он все равно может запаниковать, что приведет к тому же: функция завершится, что приведет к недействительности buf . Поскольку received_message заимствует buf , это означает received_message , что не может быть действительным после завершения функции. Но в этот момент сообщение все еще будет находиться в канале, ожидая прочтения (или извлечения получателем, делающим неизвестно что).

Поэтому ваше строительство запрещено.

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

Решение довольно простое: пусть каждая итерация создает собственную строку (копируя сообщение текущей итерации) и отправляет ее по каналу:

 tx.clone().send(received_message.to_string());
 

Кроме того, это больше замечаний о стиле / неэффективности, но:

  • clone() Включение tx полностью избыточно. Смысл наличия отправителя, Clone который может отправлять из нескольких потоков (отсюда mp в названии канала, это для нескольких производителей). Здесь у вас есть один поток, исходный отправитель работает нормально.
  • .as_slice() и .as_mut_slice() редко используются без необходимости, чего здесь нет: ссылки на массивы принуждают к срезам, поэтому вы можете просто использовать amp;mut buf и amp;buf . И почему ты взываешь Path::new к тому, что уже является путем? Это ничего не делает, но и не полезно.
  • Довольно раздражает, что в вашем фрагменте отсутствует несколько импортных файлов и, следовательно, он даже не компилируется как есть.
  • С точки зрения unixy, ошибки обычно печатаются на stderr. В Rust eprintln делает это за вас (в противном случае работает точно так println же). И я не понимаю цели маркировки лексически вложенного static pub . Поскольку static он находится внутри функции, он даже не виден братьям и сестрам функции, не говоря уже о внешних вызывающих абонентах. В результате я бы в конечном итоге получил это:
     use std::os::unix::net::UnixDatagram;
    use std::path::Path;
    use std::sync::mpsc;
    use std::str::from_utf8;
    
    fn unlink_socket (path: impl AsRef<Path>) {
        let path = path.as_ref();
        if path.exists() {
            if let Err(e) = std::fs::remove_file(path) {
                eprintln!("Couldn't remove the file: {:?}", e);
            }
        }
    }
    
    static FILE_PATH: amp;'static str = "/tmp/datagram.sock";
    pub fn tcp_datagram_server() {
        unlink_socket(FILE_PATH);
        let socket = match UnixDatagram::bind(FILE_PATH) {
            Ok(socket) => socket,
            Err(e) => {
                eprintln!("Couldn't bind: {:?}", e);
                return;
            }
        };
    
        let (tx, _) = mpsc::channel();
        let mut buf = vec![0; 1024];
        println!("Waiting for client to connect...");
        loop {
            let received_bytes = socket.recv(amp;mut buf).expect("recv function failed");
            println!("Received {:?}", received_bytes);
            let received_message = from_utf8(amp;buf).expect("utf-8 convert failed");
            tx.send(received_message.to_string());
        }
    }
     

Ответ №2:

В сообщении компилятора есть подсказка, что значения в области удаляются в порядке, противоположном тому, в котором они определены , и в примере buf определяется после tx , что означает, что они будут удалены раньше tx . Поскольку ссылка на buf (в форме received_message ) передается tx.send() , то buf должна жить дольше, чем tx, и, следовательно, переключение порядка определения исправит эту конкретную ошибку (т. Е. Переключит строки 19 и 20).