Два изменчивых заимствования происходят на одной линии?

Я пытаюсь использовать ящик для снежного кома в Rust, чтобы остановить вектор слов. Это должно быть просто, но средство проверки заимствований продолжает отклонять мой код:

// Read user input
let input = stdin();
let mut stemmer = Stemmer::new("english").unwrap();
for line in input.lock().lines() {
    let line = line.unwrap();
    let mut query: Vec<_> = line.split_whitespace().collect();
    for t in &mut query {
        *t = stemmer.stem_str(t);
    }
    // …
}

Проверка заимствования говорит, что у меня есть два изменяемых заимствования stemmer на линии *t = stemmer.stem_str(t); и отклоняет мой код. (Строка 80, где блок for line in input.lock().lines() конец.)

57  18 error    E0499  cannot borrow `stemmer` as mutable more than once at a time (first mutable borrow occurs here) (rust-cargo)
57  18 error    E0499  cannot borrow `stemmer` as mutable more than once at a time (second mutable borrow occurs here) (rust-cargo)
80   5 info     E0499  first borrow ends here (rust-cargo)

Если я позвоню stem() метод напрямую, я получаю String, но тогда я не могу просто позвонить as_str() и ожидаем назначить полученное &str вернуться к *t, поскольку контролер заимствований жалуется, что "заимствованная стоимость не живет достаточно долго".

57  18 error           borrowed value does not live long enough (temporary value created here) (rust-cargo)
57  18 info            consider using a `let` binding to increase its lifetime (rust-cargo)
57  42 info            temporary value only lives until here (rust-cargo)
80   5 info            temporary value needs to live until here (rust-cargo)

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

1 ответ

Решение

Из документацииstem_str:

Ссылка str, которую она возвращает, действительна только до тех пор, пока вы не вызовете stem или stem_str снова; таким образом, заемщик Rust не позволит вызвать одну из них, если у вас есть такая ссылка в области видимости.

Предположительно, это потому, что реализация стеммера на самом деле имеет своего рода внутренний буфер, в котором слово хранится в том виде, в котором оно содержится.

Вот почему вы не можете позвонить stem_str дважды, сохраняя ссылку на строку; это сделает недействительной первую строку!

Я не могу просто позвонить as_str() и ожидаем назначить полученное &str вернуться к *t

Компилятор снова абсолютно прав. Вы пытаетесь создать значение, взять ссылку на него, сохранить ссылку, а затем удалить значение! Это уязвимость памяти, и вы не можете сделать это.

Вместо этого соберите вектор Strings:

for line in input.lock().lines() {
    let line = line.unwrap();
    let mut query: Vec<_> = line.split_whitespace()
        .map(|t| stemmer.stem(t))
        .collect();
}

Я очень рекомендую прочитать The Rust Programming Language и понять, как работают ссылки и что они предотвращают. Делайте это до и во время чего-то сложного с владением. Эти главы специально:

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