首页 > 解决方案 > 返回包含引用的结果时“变量的寿命不够长”,但它的寿命确实足够长

问题描述

我正在实现一个小实用程序,编译器告诉我变量 (a TcpStream) 的寿命不够长,并建议我找到一种方法让它与当前的寿命一样长。

错误信息

error[E0597]: `stream` does not live long enough
  --> src/main.rs:47:35
   |
47 |         match handle_request(&mut stream){
   |                                   ^^^^^^ borrowed value does not live long enough
...
54 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 43:1...
  --> src/main.rs:43:1
   |
43 | / fn handle_array(stream: &mut BufReader<TcpStream>) -> Result<Data,Errors>
44 | | {
45 | |     let mut array: Vec<Data> = Vec::with_capacity(50);//arbitrary size, picked differently in the complete program
46 | |     for _x in 0..50 {
...  |
53 | |     Ok(Data::Array(array))
54 | | }
   | |_^

代码

具有确切问题的 Rust 操场片段

use std::collections::HashMap;
use std::io::BufReader;
use std::io::Read;
use std::net::TcpStream;

static TOKEN: &[u8; 2] = b"\r\n";

fn main() {}

#[derive(Debug, Clone)]
pub enum Data {
    String(Vec<u8>),
    Error(Vec<u8>),
    Integer(i64),
    Binary(Vec<u8>),
    Array(Vec<Data>),
    Dictionary(HashMap<String, Data>),
}

#[derive(Debug, Clone)]
pub enum Errors<'a> {
    CommandError(&'a str),
    EndOfConnection,
    NotImplemented,
}

pub fn handle_request(stream: &mut BufReader<TcpStream>) -> Result<Data, Errors> {
    //TODO handle the empty stream
    let mut buff: [u8; 1] = *b"0";
    stream.read_exact(&mut buff); //TODO: handle error here
    match &buff {
        /* part skipped, not relevant */
        b"*" => handle_array(stream),
        &[_] => Err(Errors::CommandError("Bad request")),
    }
}

/*part skipped, not relevant  */

fn handle_array(stream: &mut BufReader<TcpStream>) -> Result<Data, Errors> {
    let mut array: Vec<Data> = Vec::with_capacity(50); //arbitrary size, picked differently in the complete program
    for _x in 0..50 {
        match handle_request(&mut stream) {
            Ok(x) => array.push(x.clone()),
            Err(x) => return Err(x.clone()),
        }
    }

    Ok(Data::Array(array))
}

我真的坚持这个。

看来我不能使用 Err 的值。如果我更换

match handle_request(&mut stream){
    Ok(x) => array.push(x.clone()),
    Err(x) => return Err(x.clone()),
}

match handle_request(&mut stream){
    Ok(x) => array.push(x.clone()),
    Err(_) => return Err(Errors::NotImplemented),
}

问题解决了,但我不知道为什么。

标签: rustlifetime

解决方案


您的问题可以简化为:

struct Reader;
struct Data;
struct Errors<'a>(&'a str);

fn handle_array(stream: &mut Reader) -> Result<Data, Errors> {
    for _ in 0..0 {
        handle_request(&mut stream)?;
    }

    unimplemented!();
}

fn handle_request(_stream: &mut Reader) -> Result<Data, Errors> {
    unimplemented!()
}

fn main() {}
error[E0597]: `stream` does not live long enough
  --> src/main.rs:7:29
   |
7  |         handle_request(&mut stream)?;
   |                             ^^^^^^ borrowed value does not live long enough
...
11 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 5:1...
  --> src/main.rs:5:1
   |
5  | / fn handle_array(stream: &mut Reader) -> Result<Data, Errors> {
6  | |     for _ in 0..0 {
7  | |         handle_request(&mut stream)?;
8  | |     }
9  | |
10 | |     unimplemented!();
11 | | }
   | |_^

在体内handle_arraystream是类型&mut Reader。但是,在调用 时handle_request,您会对其进行另一个引用,创建一个&mut &mut Reader.

在代码中添加一些显式的生命周期(出于教育目的,这不会编译),代码看起来像这样:

fn handle_array<'stream>(stream: &'stream mut Reader) -> Result<Data, Errors> {
    let tmp: &'tmp mut &'stream mut Reader = &mut stream;
    if let Err(x) = handle_request(tmp)

handle_request需要 a &mut Reader,因此编译器会插入一些代码来为您对齐这两种类型。编译器必须对如何执行此转换保持保守,因此它选择较短的生命周期:

fn handle_array<'stream>(stream: &'stream mut Reader) -> Result<Data, Errors> {
    let tmp: &'tmp mut &'stream mut Reader = &mut stream;
    let tmp2: &'tmp mut Reader = tmp;
    if let Err(x) = handle_request(tmp2)

问题的下一个方面是这两个函数都使用了生命周期省略。它们的扩展形式如下:

fn handle_array<'stream>(stream: &'stream mut Reader) -> Result<Data, Errors<'stream>>

fn handle_request<'_stream>(_stream: &_stream mut Reader) -> Result<Data, Errors<'_stream>>

这意味着返回Errors的生命周期与参数的生命周期相关,但在您的情况下,参数handle_request的生命周期较短'tmp而不是生命周期'stream。这说明了为什么会出现编译器错误:您试图返回一个Errors只能存在于函数内部的变量(变量本身的生命周期stream),但您试图返回一个需要更长时间的引用。

我们可以通过只传递stream给来解决这个问题handle_request

handle_request(stream)?;

不幸的是,这只改变了错误:

error[E0499]: cannot borrow `*stream` as mutable more than once at a time
  --> src/main.rs:9:40
   |
9  |         if let Err(x) = handle_request(stream) {
   |                                        ^^^^^^ mutable borrow starts here in previous iteration of loop
...
15 | }
   | - mutable borrow ends here

这部分很难解释。看:

这是目前 Rust 的一个非常粗糙的边缘,但它越来越接近被修复!但是,现在,您有两个主要选择:

调用该函数两次

这可能不起作用,因为您无法从流中读取两次,但在其他情况下它可能有用:

fn handle_array(stream: &mut Reader) -> Result<Data, Errors> {
    let mut array = vec![];
    for _ in 0..0 {
        if handle_request(stream).is_err() {
            return handle_request(stream);
        }
        if let Ok(r) = handle_request(stream) {
            array.push(r);
        };
    }

    unimplemented!();
}

删除引用

暂时放弃在这种情况下尝试引用

struct Errors(String);

fn handle_array(stream: &mut Reader) -> Result<Data, Errors> {
    let mut array = vec![];
    for _ in 0..0 {
        array.push(handle_request(stream)?);
    }

    unimplemented!();
}

为了提高效率,我会使用迭代器编写:

fn handle_array(stream: &mut Reader) -> Result<Data, Errors> {
    let array = (0..0)
        .map(|_| handle_request(stream))
        .collect::<Result<Vec<_>, _>>()?;

    unimplemented!();
}

未来?

使用不稳定的NLL 功能和实验性的“Polonius”实现,这段代码可以工作:

struct Errors<'a>(&'a str);

fn handle_array(stream: &mut Reader) -> Result<Data, Errors> {
    let mut array = vec![];
    for _ in (0..0) {
        array.push(handle_request(stream)?);
    }

    unimplemented!();
}

这将需要一段时间才能普遍使用.​​.....


推荐阅读