当类型为“Copy”时,方法应该移动(“self”)还是借用(“&self”)类型?

When a type is `Copy` should a method move (`self`) or borrow (`&self`) the type?

提问人:scottwillmoore 提问时间:6/15/2023 最后编辑:scottwillmoore 更新时间:6/15/2023 访问量:117

问:

在 Rust 中,当类型为 Copy 时,方法应该移动 () 还是借用 () 类型?self&self

当调用非类型的方法时,move () 和 borrow () 之间存在显着差异。例如,您将无法调用两次使用 move () 的方法。但是,对于类型,移动 () 和借用 () 之间的差异变得不那么重要,至少对于调用者来说是这样。Copyself&selfselfCopyself&self

此外,由于 Rust 会自动取消引用或借用来查找方法,因此您可以以完全相同的方式调用 move () 方法或引用 () 方法。self&self

我预计每种方法几乎没有区别。当由 Rust 编译器内联时,两者似乎会生成相同的汇编代码。至少从我在 Godbolt 编译器资源管理器上的实验来看。但是,当未内联时,生成的程序集代码可能会有所不同。这是一个微不足道的例子,因此应该持保留态度。

我无法从标准库中建立通用做法,因为似乎两者都有示例。

我不认为现实世界中的每种方法之间应该有太大区别,尽管这确实让我感到好奇。每次我在一种类型上实现时,我都想知道最好的方法是什么......Copy

我确实找到了一个示例,我必须同时为值和引用实现 From,否则该示例将无法编译。我敢肯定还有更多表现出类似行为的例子,因此,这可能使得 move () 或 borrow () 的实现方法更可取。self&self

你认为什么是最好的?这有关系吗?有标准方法吗?还有其他我应该考虑的情况吗?

编辑:目前我更喜欢对类型使用移动()。selfCopy

rust move borrow-checker 取消引用 借款

评论


答:

2赞 Chayim Friedman 6/15/2023 #1

我的经验法则是:

  • 如果在非类型上实现时,此方法将消耗 ,对类型执行相同的操作,因为这样更清晰、更统一。这方面的一个例子是 上的算术方法,例如 。CopyselfCopyDurationchecked_add()

  • 如果不是,那么:对于小类型(当一个机器字节(最大 usize)肯定很小,两个通常很小,因为它们是在一对寄存器中传递的,但三个已经很大了,因为它们是在堆栈上传递的),预计在未来版本中会保持较小,按值计算,因为这样更快;对于其他类型的(大或可能变大)采取 ,因为采取会强制复制并且会(有点)慢。self&selfself

    第一种类型的一个例子是关于整数、浮点数和字符的所有方法(嗯,几乎所有方法,有些是出于遗留原因而采用的)。第二种类型的示例是 on 或 上的方法。char&selfDurationIpAddr

From是另一回事;为自有和引用实现它是有意义的。

评论

0赞 scottwillmoore 6/15/2023
谢谢你的回答!有什么不同?我不太明白为什么 Rust 不会自动取消引用或借用它。该文档似乎表明编译器应该构建所有“候选接收者类型”的列表,并尝试在任何“可见特征”中搜索方法。From
0赞 scottwillmoore 6/15/2023
另外,如果是 a 和 a,你认为它的所有方法都应该用 来实现,而不是只用一些方法实现,比如 .也许改变是可取的,但不能,因为它现在将是一个突破性的改变?Durationu64u32selfchecked_add
0赞 Chayim Friedman 6/15/2023
@scottwillmoore 有什么不同?它不是自动引用的事实。而关于,不一定;很多时候,按值传递一对标量会更快,因为它们是在寄存器中传递的,但并非总是如此。对于一个标量,按值传递总是比按引用传递便宜。FromDuration
1赞 Chayim Friedman 6/15/2023
@scottwillmoore 还要考虑 32 位平台,其中不是标量。通常我们不会考虑它们,但 std 应该适合所有人。现在,这只是纳秒的问题,所以这可能并不重要:这只是一个好习惯。u64
0赞 scottwillmoore 6/15/2023
只是为了跟进(也许这应该是一个单独的问题),您能否扩展一下为什么不“自动引用”。从参考文献来看,它似乎是应该的。我的逻辑是:构建一个“候选接收器类型列表”,其中应包含 ,并且当按值或引用调用时。对于这些类型中的每一种,都会查找该方法,因为它是“可见特征”。因此,我们难道不应该只要求通过值或引用来实现吗?FromCardCard&CardInto<char>::into()From