Async fn, вызывающий split, генерирует ошибку «заимствованное значение не живет достаточно долго»

Я создал надуманный пример, чтобы понять ошибку, которую я вижу с асинхронной функцией, используя tokio:

pub async fn unique_words_async() -> io::Result<u32> {
    let file = File::open("sample.txt").await?;
    let reader = BufReader::new(file);
    let mut lines = reader.lines();

    let mut h = HashMap::new();
    h.insert("word", true);
    while let Some(line) = lines.next_line().await? {
        let mut split = line.trim().split(' ');
        while let Some(word) = split.next() {
            h.insert(word, true);
        }
    }
    Ok(h.len() as u32)
}

вот точная ошибка:

16 |         let mut split = line.trim().split(' ');
   |                         ^^^^^^^^^^^ borrowed value does not live long enough
...
20 |     }
   |     - `line` dropped here while still borrowed
21 |     Ok(h.len() as u32)
   |        ------- borrow later used here

смотрите бег в прямом эфире в детская площадка

Неасинхронный код для выполнения той же задачи отлично работает (см. детская площадка):

pub fn unique_words() -> u32 {
    let input = "this is one line\nhere is another line";
    let mut lines = input.lines();
    let mut h = std::collections::HashMap::new();
    while let Some(line) = lines.next() {
        let mut split = line.split(' ');
        while let Some(word) = split.next() {
            h.insert(word, true);
        }
    }
    h.len() as u32
}
Асинхронная передача данных с помощью sendBeacon в JavaScript
Асинхронная передача данных с помощью sendBeacon в JavaScript
В современных веб-приложениях отправка данных из JavaScript на стороне клиента на сервер является распространенной задачей. Одним из популярных...
0
0
30
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш неасинхронный перевод неверен. Если вы будете использовать File с BufReader, у вас будет та же ошибка:

pub fn unique_words() -> std::io::Result<u32> {
    use std::io::BufRead;

    let file = std::fs::File::open("sample.txt")?;
    let reader = std::io::BufReader::new(file);
    let mut lines = reader.lines();

    let mut h = std::collections::HashMap::new();
    while let Some(line) = lines.next().transpose()? {
        let mut split = line.split(' ');
        while let Some(word) = split.next() {
            h.insert(word, true);
        }
    }
    Ok(h.len() as u32)
}
error[E0597]: `line` does not live long enough
  --> src/main.rs:10:25
   |
10 |         let mut split = line.split(' ');
   |                         ^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
14 |     }
   |     - `line` dropped here while still borrowed
15 |     Ok(h.len() as u32)
   |        ------- borrow later used here

Детская площадка.

Это связано с тем, что Lines::next() возвращает Option<io::Result<String>>, а String отбрасывается в конце каждого цикла цикла. Однако str::trim() и str::split() берут &str и производят &str с тем же временем жизни (поскольку они только нарезают строку, а не изменяют ее). Таким образом, вы вставляете h a &str со временем жизни в один цикл, то есть висячую ссылку.

Причина, по которой это работало с str::lines(), заключается в том, что std::str::Lines::next() возвращает Option<&str> со ссылкой, привязанной к исходному &str, то есть 'static.

Самый простой способ исправить это — преобразовать &str в собственный String:

h.insert(word.to_owned(), true);

Детская площадка.

Другие вопросы по теме