因此,我看过一个this问题,该问题解释了serde_json
如何既可以通过引用获取读者/作家,也可以通过获取所有权来实现。足够公平。
但是我不知道该方法如何用于Write
-所有Write
方法都需要一个&mut self
,所以我认为如果我通过只知道其方法的方法参数是对Write
的引用,它对此无能为力。但是,即使我将非mut引用传递给以某种方式最终写入被引用文件的方法,该示例也可以编译并正常工作:
extern crate serde_json;
use std::fs::OpenOptions;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open("/tmp/serde.json")?;
// why does this work?
serde_json::to_writer(&file, &10)?;
Ok(())
}
我正在传递&File
-如预期的那样,如果我要在Write
上直接调用File
的任何方法,将无法正常工作:
use std::io::{self, Write};
use std::fs::OpenOptions;
fn main() -> io::Result<()> {
let file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open("/tmp/wtf")?;
let file_ref = &file;
// this complains about not having a mutable ref as expected
file_ref.write(&[1,2,3])?;
Ok(())
}
error[E0596]: cannot borrow `file_ref` as mutable, as it is not declared as mutable
--> test.rs:12:5
|
10 | let file_ref = &file;
| -------- help: consider changing this to be mutable: `mut file_ref`
11 | // this complains about not having a mutable ref as expected
12 | file_ref.write(&[1,2,3])?;
| ^^^^^^^^ cannot borrow as mutable
那有什么用呢? serde_json
是否以某种方式破坏了类型系统,或者这是类型系统的故意特征?如果是后者,它如何工作以及为什么这样工作?
答案 0 :(得分:5)
serde_json::to_writer
的第一个参数接受实现Write
的任何类型。 One such value is &File
。
这可能令人惊讶,但是File
的文档明确声明了 reference 对文件的可变性与文件是否更改无关。
请注意,尽管读写方法需要
&mut File
,但是由于Read
和Write
的接口,&File
的持有者仍然可以修改文件,可以通过采用&File
的方法,也可以通过检索基础OS对象并以此方式修改文件。此外,许多操作系统允许通过不同的进程同时修改文件。避免假定持有&File
意味着文件不会更改。
您可能会问-等一下,我认为Write
上的方法花费了&mut self
?他们做到了!但是在这种情况下,Write
是为&File
实现的,因此传递给Write
的类型在某种程度上令人困惑,是&mut &File
(对不变引用的可变引用)。 / p>
这说明了为什么您的第二个示例无法编译-您需要能够对&file
进行可变引用,并且在绑定可变之前就不能这样做。这是要实现的重要事情-绑定的可变性和绑定值的可变性不一定相同。
在运行代码时,错误消息中会对此进行提示:
error[E0596]: cannot borrow `file_ref` as mutable, as it is not declared as mutable
--> src/lib.rs:11:5
|
10 | let file_ref = &file;
| -------- help: consider changing this to be mutable: `mut file_ref`
11 | file_ref.write(&[1,2,3])?;
| ^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
如果将let file_ref = &file;
更改为let mut file_ref = &file;
,则会编译代码。