是否有一种很好的方法来使用Rust处理结构中包含的文件的所有权?作为一个简化的示例,请考虑:
// Buffered file IO
use std::io::{BufReader,BufRead};
use std::fs::File;
// Structure that contains a file
#[derive(Debug)]
struct Foo {
file : BufReader <File>,
data : Vec <f64>,
}
// Reads the file and strips the header
fn init_foo(fname : &str) -> Foo {
// Open the file
let mut file = BufReader::new(File::open(fname).unwrap());
// Dump the header
let mut header = String::new();
let _ = file.read_line(&mut header);
// Return our foo
Foo { file : file, data : Vec::new() }
}
// Read the remaining foo data and process it
fn read_foo(mut foo : Foo) -> Foo {
// Strip one more line
let mut header_alt = String::new();
let _ = foo.file.read_line(&mut header_alt);
// Read in the rest of the file line by line
let mut data = Vec::new();
for (lineno,line) in foo.file.lines().enumerate() {
// Strip the error
let line = line.unwrap();
// Print some diagnostic information
println!("Line {}: val {}",lineno,line);
// Save the element
data.push(line.parse::<f64>().unwrap());
}
// Export foo
Foo { data : data, ..foo}
}
fn main() {
// Initialize our foo
let foo = init_foo("foo.txt");
// Read in our data
let foo = read_foo(foo);
// Print out some debugging info
println!("{:?}",foo);
}
这当前会导致编译错误:
error[E0382]: use of moved value: `foo.file`
--> src/main.rs:48:5
|
35 | for (lineno,line) in foo.file.lines().enumerate() {
| -------- value moved here
...
48 | Foo { data : data, ..foo}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move
|
= note: move occurs because `foo.file` has type `std::io::BufReader<std::fs::File>`, which does not implement the `Copy` trait
error: aborting due to previous error
For more information about this error, try `rustc --explain E0382`.
error: Could not compile `rust_file_struct`.
To learn more, run the command again with --verbose.
而且,可以肯定的是,这是有道理的。在这里,lines()
拥有缓冲文件的所有权,因此我们不能在返回值中使用该值。让我感到困惑的是处理这种情况的更好方法。当然,在for循环之后,该文件将被使用,因此它实际上无法使用。为了更好地表示这一点,我们可以将文件表示为Option <BufReader <File>>
。但是,这会引起一些麻烦,因为在read_line
内部的第二个read_foo
调用需要对file
的可变引用,而且我不确定如何获取包装在Option
。是否有处理所有权的好方法?
要清楚,这是一个简化的示例。在实际的用例中,有几个文件以及其他数据。我采用这种方式构建事物,因为它表示来自命令行选项的配置。有些选项是文件,有些是标志。无论哪种情况,我都希望尽早进行一些处理,但不是全部处理,以引发适当的错误。
答案 0 :(得分:1)
我认为您正在使用Option
结构中的Foo
。假设结构变为:
struct Foo {
file : Option<BufReader <File>>,
data : Vec <f64>,
}
以下代码是可能的解决方案:
// Reads the file and strips the header
fn init_foo(fname : &str) -> Foo {
// Open the file
let mut file = BufReader::new(File::open(fname).unwrap());
// Dump the header
let mut header = String::new();
let _ = file.read_line(&mut header);
// Return our foo
Foo { file : Some(file), data : Vec::new() }
}
// Read the remaining foo data and process it
fn read_foo(foo : Foo) -> Option<Foo> {
let mut file = foo.file?;
// Strip one more line
let mut header_alt = String::new();
let _ = file.read_line(&mut header_alt);
// Read in the rest of the file line by line
let mut data = Vec::new();
for (lineno,line) in file.lines().enumerate() {
// Strip the error
let line = line.unwrap();
// Print some diagnostic information
println!("Line {}: val {}",lineno,line);
// Save the element
data.push(line.parse::<f64>().unwrap());
}
// Export foo
Some(Foo { data : data, file: None})
}
在这种情况下,由于read_foo
可能是Foo
,因此file
返回可选的None
。
IMO,在旁注中,除非您绝对需要BufReader
和Foo
一起出游,否则我将其丢弃。正如您已经发现的那样,调用lines
会导致移动,这使其很难保留在另一个结构中。作为建议,您可以将file
字段简化为String
,以便始终可以导出BufReader
并在需要时读取文件。
例如,这是一个解决方案,其中文件名(即&str)可以转换为Foo
,而所有的行处理都在构造该结构之前完成。
// Buffered file IO
use std::io::{BufReader,BufRead};
use std::fs::File;
// Structure that contains a file
#[derive(Debug)]
struct Foo {
file : String,
data : Vec <f64>,
}
trait IntoFoo {
fn into_foo(self) -> Foo;
}
impl IntoFoo for &str {
fn into_foo(self) -> Foo {
// Open the file
let mut file = BufReader::new(File::open(self).unwrap());
// Dump the header
let mut header = String::new();
let _ = file.read_line(&mut header);
// Strip one more line
let mut header_alt = String::new();
let _ = file.read_line(&mut header_alt);
// Read in the rest of the file line by line
let mut data = Vec::new();
for (lineno,line) in file.lines().enumerate() {
// Strip the error
let line = line.unwrap();
// Print some diagnostic information
println!("Line {}: val {}",lineno,line);
// Save the element
data.push(line.parse::<f64>().unwrap());
}
Foo { file: self.to_string(), data }
}
}
fn main() {
// Read in our data from the file
let foo = "foo.txt".into_foo();
// Print out some debugging info
println!("{:?}",foo);
}
在这种情况下,无需担心BufReader
的所有权,因为它是在同一函数中创建,使用和丢弃的。当然,我不完全了解您的用例,因此这可能不适合您的实现。