Проблема заимствования стоимости в Rust с помощью bufreader

Я искал (и, между прочим, нашел) множество проблем, которые в чем-то идентичны, но я изо всех сил пытаюсь найти способ заставить код ниже работать в рустоматическом стиле. Я создал эквивалент grep, который работает, теперь я хотел улучшить его с помощью bufreader вместо загрузки всего текста (на самом деле файла журнала). Но у меня проблема с проверкой заимствований: я не могу работать с тем, что пытаюсь. Я пробовал использовать фрагменты, но появляются и другие ошибки, поэтому я немного не понимаю, как это сделать.

use::foo;
use::clap::Parser;
use::std::io::{BufRead,BufReader};
use::std::fs::File;
/// Search for a pattern in a file and display the lines that contain it.
#[derive(Parser)]
struct Cli {
    /// the pattern that we want to search
    #[arg(long)]
    pattern : String,
    /// the path where the file is located in which we want to search the pattern
    #[arg(long)]
    path: std::path::PathBuf,
}

fn main(){
    let args = Cli::parse();
    // let content = std::fs::read_to_string(&args.path).expect("could not read file");
    let content = File::open(&args.path).expect("could not read file");
    let reader = BufReader::new(content);
    let lines = reader.lines();

    for line in lines {
        if line.unwrap().contains(&args.pattern){
            println!("{:?}",line);
        }
    }
}

Ошибка:

error[E0382]: borrow of moved value: `line`
    --> src/main.rs:25:29
     |
23   |     for line in lines {
     |         ---- move occurs because `line` has type `Result<std::string::String, std::io::Error>`, which does not implement the `Copy` trait
24   |         if line.unwrap().contains(&args.pattern){
     |            ---- -------- `line` moved due to this method call
     |            |
     |            help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
25   |             println!("{:?}",line);
     |                             ^^^^ value borrowed here after move

Думаю, вам стоит использовать line.as_ref().unwrap().contains(...)

yolenoyer 01.08.2024 10:50

кажется, это действительно работает. Так что хитрость здесь заключается не в использовании &, а в as_ref. Дополнительную информацию я нашел на doc.rust-lang.org/std/convert/trait.AsRef.html#implementors и users.rust-lang.org/t/what-is-the-difference-between- как ссылка/‌​…

Chris Vermeijlen 01.08.2024 10:55

Вместо того, чтобы разворачивать каждый результат из lines(), вы можете перебирать отфильтрованные строки, которые гарантированно будут Result::ok().

Richard Neumann 01.08.2024 11:05
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Сохранить строку в переменной? contains() нужна только ссылка:

for line in lines {
    let line = line.unwrap();
    if line.contains(&args.pattern){
        println!("{:?}",line);
    }
}
Ответ принят как подходящий

Причина вашей ошибки следующая.

Поскольку вы используете BufReader::lines , он возвращает объект Lines . Как сказано в документации он реализуетIterator, который дает Result<String, Error> тип данных. После .unwrap() вы разворачиваете внутренний String и вызываете contains в строке if line.unwrap().contains(&args.pattern){, после чего этот объект-строка (имеющий тип Result<String, Error>) перемещается и больше не может использоваться. Итак, вы получаете упомянутую ошибку в println!. Это типичный случай с проверкой заимствований в Rust. Итак, есть как минимум два способа решить эту проблему:

  1. Доступ к .unwrap() по ссылке (возможен, ведь для .contains достаточно иметь ссылку на объект (интерфейс смотрите здесь: contains(&self ...)https://doc.rust-lang.org/std/string/struct. String.html#method.contains). Итак, все, что вам нужно, это вызвать line.as_ref().unwrap().contains(...) в операторе if.

  2. Извлеките строковый объект один раз, а затем выполните для него .contains и println!. Итак, вы можете написать что-то вроде:

// The variable `line` is MOVED here, but not `extracted_str`
let extracted_str = line.unwrap();

// The object extracted_str is NOT moved here because now the compiler understands
// that `.contains` is accessed it by the reference
if extracted_str.contains(args.pattern) {

    // extracted_str is moved here but you should not care of it
    // because you don't use extracted_str after printing,
    // but if you needed to used it, likely you'd have to clone the string
    // using extracted_str.clone() in the `println!` macros
    println!("{}", extracted_str);
}

Короче говоря, line.unwrap() перемещает весь объект (потому что имеет интерфейс unwrap(self) без &) line включая внутреннюю строку, поэтому вы не можете снова использовать line ниже. Но если на этом этапе вы сохраните строку разворачивания, вы сможете использовать ее много раз, если внутренние методы поддерживают &self в интерфейсе (обычно это справедливо для большинства методов).

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