为什么可以在不可变文件引用上实现读取操作?

15
如果您查看 Read的文档, 大多数方法都接受一个&mut self. 这很有道理,因为从某个地方读取通常会更新内部偏移量,以便下一次读取返回不同的数据。但是,以下内容可以编译:
use std::io::Read;
use std::fs::File;

fn main() {
    let file = File::open("/etc/hosts").unwrap();
    let vec = &mut Vec::new();
    (&file).read_to_end(vec).unwrap();
    println!("{:?}", vec);
}

文件不可变,但数据肯定被读入了。这让我感到不正确。有人指出存在一个 impl<'a> Read for &'a File, 但似乎仍在改变不可变实例的事实仍然很奇怪。

1
答案为什么我可以在引用上调用File.take()?是否已经解决了这个问题,或者您是在问为什么在&File上实现了Read? - user395760
2
@delnan 这里的问题是:为什么 &file 就足够了?人们应该期望需要 &mut file - mdup
1
@delnan(我没有问这个问题,但)我认为这并不能回答这个问题。肯定还有其他的问题。read_to_end应该适用于&mut File&mut &File,但(&file)都不是。 - fjh
2
@fjh:我相信a.read_to_end(vec)会调用Read::read_to_end(&mut a, vec),也就是说该方法已经看到了&mut &file - kennytm
@delnan 这两个问题源于同一来源,但为了有一些有用的补充,我对“为什么”和“如何”在不可变引用上实现Read都很好奇,因此我已经调整了问题。 - Shepmaster
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - xophos
1个回答

14
正如 @kennytm 指出的那样,a.read_to_end(vec) 等价于 Read::read_to_end(&mut a, vec),因此 (&file).read_to_end(vec) 展开为 Read::read_to_end(&mut &file, vec)。在后面的表达式中,&file 是一个新的临时值,类型为 &File。对于表达式(例如 &mut 42),我们可以取得可变引用,这没有任何问题。事实上,表达式是不可变值的引用并不重要,因为我们不能通过 &mut &T 实际改变该值。
关于为什么不需要将 File 设为可变的问题: File 基本上只是一个新型的文件描述符,即打开文件表中的索引,该表由操作系统管理。 read 和相关函数根本不会改变此描述符,这就是为什么不需要改变 File 的原因。当然,有一些变化正在发生,但这是由操作系统在其自己的数据结构上进行的,而不是在您的用户空间 Rust 代码中进行的。

5
在“变异[...]是由操作系统对其自己的数据结构进行处理,而不是在您的用户态 Rust 代码中进行处理”的情况下,给出一个+1。我曾认为有一个(不可变的)&File表示您的文件不会发生任何变化;但这个答案表明这并非如此,这在我看来很反直觉。也许这值得在文档中进行澄清。 - mdup
1
@mdup 或许最好我们把 File 当作某种 cell 来考虑。 - Shepmaster
2
@Shepmaster:是的,就像一个文件有自己内置的互斥锁一样。操作系统会负责它的同步。 - sellibitze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接