Я искал (и, между прочим, нашел) множество проблем, которые в чем-то идентичны, но я изо всех сил пытаюсь найти способ заставить код ниже работать в рустоматическом стиле. Я создал эквивалент 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
кажется, это действительно работает. Так что хитрость здесь заключается не в использовании &, а в as_ref. Дополнительную информацию я нашел на doc.rust-lang.org/std/convert/trait.AsRef.html#implementors и users.rust-lang.org/t/what-is-the-difference-between- как ссылка/…
Вместо того, чтобы разворачивать каждый результат из lines()
, вы можете перебирать отфильтрованные строки, которые гарантированно будут Result::ok()
.
Сохранить строку в переменной? 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. Итак, есть как минимум два способа решить эту проблему:
Доступ к .unwrap()
по ссылке (возможен, ведь для .contains
достаточно иметь ссылку на объект (интерфейс смотрите здесь: contains(&self ...)
https://doc.rust-lang.org/std/string/struct. String.html#method.contains). Итак, все, что вам нужно, это вызвать line.as_ref().unwrap().contains(...)
в операторе if.
Извлеките строковый объект один раз, а затем выполните для него .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
в интерфейсе (обычно это справедливо для большинства методов).
Думаю, вам стоит использовать
line.as_ref().unwrap().contains(...)