生 rust :如何修复借来的价值活得不够长

plupiseo  于 2023-03-08  发布在  其他
关注(0)|答案(3)|浏览(128)

我有一个简单的客户端/服务器应用程序。我在服务器端接收来自客户端的消息,但我想将该响应发送到从服务器到其他文件的通道,我收到错误"借用的值不够长"。
我已经在堆栈溢出中搜索了类似的问题,但是没有对生存期有足够的理解。有关于这个主题的好的文档或者简单的例子吗?
现在,如果有人可以帮助我修复这个代码(可能是编辑代码中需要修复的部分),这将是有帮助的。
先谢了。

服务器端:

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: &'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: &'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
zpqajqem

zpqajqem1#

现在,如果有人可以帮助我修复这个代码(可能是编辑代码中需要修复的部分),这将是有帮助的。
send确实做了它所说的事情,它通过通道 * 发送 * 参数。这意味着数据必须存活足够长的时间,并"永远"保持有效(它需要在通道中存活并有效,以及当接收方从通道中获取数据时)。
这里的情况不是这样的。rustc无法理解函数永远不会返回,并且无论如何它都会死机,结果都是一样的:函数将终止,这将使buf无效。由于received_message借用buf,这意味着received_message在函数终止后不能有效。但此时消息仍将在通道中等待读取(或被接收方检索,谁知道接收方在做什么)。
因此你的建筑是不允许的。
第二个问题是你在每个循环中都会覆盖缓冲区数据,这和你在上一次迭代中发送的消息一样,也是不正确的,尽管Rust也不允许你这么做:如果您解决了第一个错误,它会告诉您有一个未完成的共享借用(通过通道发送的消息),因此您不能在下一次迭代中修改后备缓冲区。
解决办法很简单:让每个迭代创建一个 * owned * 字符串(复制当前迭代的消息)并通过通道发送:

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

此外,这些更多的是风格/效率低下的评论,但:

  • tx上的clone()是完全冗余的。发送方是Clone的目的是能够从多个线程发送(因此通道名称中的 * mp * 表示 * 多个生产者 *)。这里有一个线程,原始发送方工作正常。
  • 除非必要,否则很少使用.as_slice().as_mut_slice(),此处没有:数组引用强制指向切片,所以你可以只使用&mut buf&buf,为什么要在已经是路径的东西上调用Path::new,它什么都不做,也没有用。
  • 代码段缺少多个导入,因此甚至不能按原样编译,这是相当烦人的。
  • 从unixy的Angular 来看,错误通常打印在stderr上。(其他方面的工作方式与println相同)。而且我不明白标记词法嵌套的staticpub的目的。由于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: &'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(&mut buf).expect("recv function failed");
        println!("Received {:?}", received_bytes);
        let received_message = from_utf8(&buf).expect("utf-8 convert failed");
        tx.send(received_message.to_string());
    }
}
m3eecexj

m3eecexj2#

编译器消息中有一个提示,即作用域中的值将按照与定义顺序相反的顺序删除,在本例中,buf定义在tx之后,这意味着它将在tx之前 * 删除。(以received_message的形式)被传递到tx.send(),则buf应该比TX存活得更长,因此切换定义顺序将修复该特定错误(即,切换第19和20行)。

5vf7fwbs

5vf7fwbs3#

这里的答案是真诚的,但也有一些情况(公认的罕见),一个值永远不会超出范围,因为你的函数是要永远存在的,而Rust的借用检查器在这种情况下无法工作,因为Rust的引用不是指针。
如果你从逻辑上考虑你的代码并且绝对肯定这是事实,那么--风险自负--你可以将你的指针强制转换为一个原始指针。
buf.as_slice() as *const u8
如果它有你需要使用的方法,你需要将它强制转换回一个引用来使用它们,例如。

let st = unsafe {
    st.as_mut().expect("invalid state") // we panic here because if this is actually reached then the program can't run anyways and we have bigger problems
};

相关问题