传递可变引用总是比创建和返回拥有的值更可取吗?

Is it always preferable to pass in a mutable reference vs creating and returning an owned value?

从像 Python 这样的动态语言转向 Rust,我不习惯这样的编程模式,在这种模式中,您提供一个带有对空数据结构的可变引用的函数,然后由该函数填充它。一个典型的例子是将文件读入 String:

let mut f = File::open("file.txt").unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();

在我 Python 习惯的眼睛看来,API 您只需在函数中创建一个拥有的值并将其作为 return 值移出看起来更直观/符合人体工程学/ 你有什么:

let mut f = File::open("file.txt").unwrap();
let contents = f.read_to_string().unwrap();

既然Rust标准库走的是前者的道路,我想一定是有原因的。

使用参考模式总是更可取吗?如果是这样,为什么? (性能原因?具体是什么?)如果不是,我如何发现它可能有益的情况?除了填充结果数据结构(如上面的第一个示例中,其中 .read_to_string() return 读取的字节数)之外,当我还想 return 另一个值时,它最有用吗?为什么不使用元组?只是个人喜好问题吗?

如果 read_to_string 想要 return 拥有的 String,这意味着它必须在每次调用时分配一个新的 String。此外,由于 Read 实现并不总是知道要读取多少数据,因此它可能必须多次逐步重新分配正在进行的工作 String。这也意味着每个临时 String 都必须返回到要销毁的分配器。

这很浪费。 Rust 是一种系统编程语言。系统编程语言厌恶浪费。

相反,调用者负责分配和提供缓冲区。如果您只调用 read_to_string 一次,则不会发生任何变化。但是,如果你调用它 more 不止一次,你可以多次重复使用同一个缓冲区,而无需常量 allocate/resize/deallocate 循环。虽然它不适用于这种特定情况,但可以设计类似的接口来支持堆栈缓冲区,这意味着在某些情况下您可以完全避免堆activity。

让调用者传入缓冲区比替代方案更灵活。