如何决定函数输入参数何时应该是引用?
How to decide when function input params should be references or not?
写函数时,如何判断传入参数是引用还是消费?
例如,我应该这样做吗?
fn foo(val: Bar) -> bool { check(val) } // version 1
或者改用引用参数?
fn foo(val: &Bar) -> bool { check(*val) } // version 2
在客户端,如果我只有第二个版本但想消耗我的价值,我必须做这样的事情:
// given in: Bar
let out = foo(&in); // using version 2 but wanting to consume ownership
drop(in);
另一方面,如果我只有第一个版本但想保留我的参考,我必须这样做:
// given in: &Bar
let out = foo(in.clone()); // using version 1 but wanting to keep reference alive
那么哪个是首选,为什么?
做出这个选择是否有任何性能方面的考虑?或者编译器是否使它们在性能方面等效,如何实现?
你什么时候想提供这两个版本(通过特征)?在那些时候,您如何为这两个函数编写底层实现——您是在每个方法签名中复制逻辑,还是您有一个代理到另一个?哪个到哪个,为什么?
Rust 的目标是在没有内存问题的情况下获得与 C/C++ 相似的性能和语法。为此,它避免了 The Rust Book 中的 garbage collection and instead enforces a particular strict memory model of "ownership" and "borrowing". These are critical concepts in Rust. I would suggest reading Understanding Ownership 之类的事情。
内存所有权的规则是...
- Rust 中的每个值都有一个称为其所有者的变量。
- 一次只能有一个所有者。
- 当所有者超出范围时,该值将被删除。
强制单一所有者避免了 C 和 C++ 程序典型的许多错误和并发症,同时避免了运行时复杂和缓慢的内存管理。
你不能仅靠它走得太远,所以 Rust 提供了参考。引用允许函数安全地“借用”数据而无需取得所有权。您可以根据需要或者个不可变引用,或只有一个可变引用。
当应用于函数调用时,传递值会将所有权传递给函数。传递引用是“借用”,保留所有权。
了解所有权、借贷以及以后的 lifetimes 真的非常重要。但这里有一些经验法则。
- 如果您的函数需要获取数据的所有权,请按值传递。
- 如果你的函数只需要读取数据,传递一个引用。
- 如果您的函数需要更改数据,请传递可变引用。
注意其中没有的内容:性能。让编译器来处理。
假设check
只是读取数据,检查没问题,应该可以参考一下。所以你的例子是...
fn foo(val: &Bar) -> bool { check(val) }
On the client side, if I only had the second version but wanted to have my value consumed...
没有理由需要一个需要引用的函数来做到这一点。如果管理内存是函数的工作,则将所有权传递给它。如果不是,则管理您的内存不是它的工作。
也不需要手动调用drop
。您只需让变量超出范围,它就会自动删除。
And when would you want to offer both versions (via traits)?
你不会。如果一个函数可以获取引用,那么它就没有理由获取所有权。
如果函数需要所有权,您应该按值传递。如果函数只需要一个引用,你应该通过引用传递。
当函数运行不需要时按值 fn foo(val: Bar)
传递可能需要用户克隆该值。在这种情况下,首选通过引用传递,因为可以避免克隆。
当函数需要所有权时,通过引用传递 fn foo(val: &Bar)
将要求它复制或克隆该值。在这种情况下,按值传递是首选,因为它让用户可以控制现有值的所有权是转移还是克隆。该函数不必做出该决定,并且可以避免克隆。
有一些例外,像 i32
这样的简单原语可以按值传递而不会造成任何性能损失,并且可能更方便。
And when would you want to offer both versions (via traits)?
您可以使用 Borrow
特征:
fn foo<B: Borrow<Bar>>(val: B) -> bool {
check(val.borrow())
}
let b: Bar = ...;
foo(&b); // both of
foo(b); // these work
写函数时,如何判断传入参数是引用还是消费?
例如,我应该这样做吗?
fn foo(val: Bar) -> bool { check(val) } // version 1
或者改用引用参数?
fn foo(val: &Bar) -> bool { check(*val) } // version 2
在客户端,如果我只有第二个版本但想消耗我的价值,我必须做这样的事情:
// given in: Bar
let out = foo(&in); // using version 2 but wanting to consume ownership
drop(in);
另一方面,如果我只有第一个版本但想保留我的参考,我必须这样做:
// given in: &Bar
let out = foo(in.clone()); // using version 1 but wanting to keep reference alive
那么哪个是首选,为什么?
做出这个选择是否有任何性能方面的考虑?或者编译器是否使它们在性能方面等效,如何实现?
你什么时候想提供这两个版本(通过特征)?在那些时候,您如何为这两个函数编写底层实现——您是在每个方法签名中复制逻辑,还是您有一个代理到另一个?哪个到哪个,为什么?
Rust 的目标是在没有内存问题的情况下获得与 C/C++ 相似的性能和语法。为此,它避免了 The Rust Book 中的 garbage collection and instead enforces a particular strict memory model of "ownership" and "borrowing". These are critical concepts in Rust. I would suggest reading Understanding Ownership 之类的事情。
内存所有权的规则是...
- Rust 中的每个值都有一个称为其所有者的变量。
- 一次只能有一个所有者。
- 当所有者超出范围时,该值将被删除。
强制单一所有者避免了 C 和 C++ 程序典型的许多错误和并发症,同时避免了运行时复杂和缓慢的内存管理。
你不能仅靠它走得太远,所以 Rust 提供了参考。引用允许函数安全地“借用”数据而无需取得所有权。您可以根据需要或者个不可变引用,或只有一个可变引用。
当应用于函数调用时,传递值会将所有权传递给函数。传递引用是“借用”,保留所有权。
了解所有权、借贷以及以后的 lifetimes 真的非常重要。但这里有一些经验法则。
- 如果您的函数需要获取数据的所有权,请按值传递。
- 如果你的函数只需要读取数据,传递一个引用。
- 如果您的函数需要更改数据,请传递可变引用。
注意其中没有的内容:性能。让编译器来处理。
假设check
只是读取数据,检查没问题,应该可以参考一下。所以你的例子是...
fn foo(val: &Bar) -> bool { check(val) }
On the client side, if I only had the second version but wanted to have my value consumed...
没有理由需要一个需要引用的函数来做到这一点。如果管理内存是函数的工作,则将所有权传递给它。如果不是,则管理您的内存不是它的工作。
也不需要手动调用drop
。您只需让变量超出范围,它就会自动删除。
And when would you want to offer both versions (via traits)?
你不会。如果一个函数可以获取引用,那么它就没有理由获取所有权。
如果函数需要所有权,您应该按值传递。如果函数只需要一个引用,你应该通过引用传递。
当函数运行不需要时按值 fn foo(val: Bar)
传递可能需要用户克隆该值。在这种情况下,首选通过引用传递,因为可以避免克隆。
当函数需要所有权时,通过引用传递 fn foo(val: &Bar)
将要求它复制或克隆该值。在这种情况下,按值传递是首选,因为它让用户可以控制现有值的所有权是转移还是克隆。该函数不必做出该决定,并且可以避免克隆。
有一些例外,像 i32
这样的简单原语可以按值传递而不会造成任何性能损失,并且可能更方便。
And when would you want to offer both versions (via traits)?
您可以使用 Borrow
特征:
fn foo<B: Borrow<Bar>>(val: B) -> bool {
check(val.borrow())
}
let b: Bar = ...;
foo(&b); // both of
foo(b); // these work