英文:
Are there differences between .to_owned(), .clone() and dereferencing (*)?
问题
我看到:
o: [7, 2, 3]
c: [8, 2, 3]
d: [9, 2, 3]
a: [1, 2, 3]
这三种方法都克隆了 a
的内容。
有什么区别吗?
英文:
With this code on the rust playground:
fn main() {
let a = &[1, 2, 3];
let mut o = a.to_owned();
let mut c = a.clone();
let mut d = *a;
o[0] = 7;
c[0] = 8;
d[0] = 9;
println!("o: {:?}", o);
println!("c: {:?}", c);
println!("d: {:?}", d);
println!("a: {:?}", a);
}
I see:
o: [7, 2, 3]
c: [8, 2, 3]
d: [9, 2, 3]
a: [1, 2, 3]
All three methods clone the contents of a
.
Are there any differences?
答案1
得分: 4
对于Copy
实例的引用没有区别。
这是因为对于Copy
类型:
*ref_to_val
返回val
的位拷贝(不使旧值无效)。- 对于
Copy
类型,派生的Clone
实现将使用*
来实现.clone()
。 - 对于
Clone
类型,通用的ToOwned
实现将使用.clone()
来实现.to_owned()
。
所以最终都归结为位拷贝。
这是你的情况。
如果你想了解特质Deref
、Clone
、ToOwned
如何用于!Copy
类型,你需要了解它们的目的并阅读特定于类型的文档。
我认为String
和str
类型是非常典型的!Copy
类型,它们将帮助你理解这种魔法。例如,str
是ToOwned
,但不是Deref
或Clone
。你的示例对于这些类型不起作用。
英文:
For a reference to an instance of Copy
there is no difference.
That is because for Copy
types:
*ref_to_val
returns a bitwise copy ofval
(and doesn't invalidate old value).- The derived
Clone
impl. forCopy
types will implement.clone()
using*
. - The blanket
ToOwned
impl. forClone
types will implement.to_owned()
using.clone()
.
So it all boils down to bitwise copy.
This is your case.
If you want to understand how traits Deref
, Clone
, ToOwned
work for !Copy
types, you have to understand their purpose and read type-specific docs.
I believe String
and str
types are very representative !Copy
types that will help you understand the magic. Eg. str
is ToOwned
, but not Deref
or Clone
. Your example won't work for these types.
答案2
得分: 2
假设所有相关的特性都被合理实现,这三种方法都会产生相同的 结果,并且在启用优化编译时应该同样高效。它们的区别在于在什么条件下允许使用,以及它们提供了什么保证。因此,如果你正在编写 通用 代码(可以处理许多不同类型的代码),可能会关系你选择哪一个,但如果代码不是通用的,除了可读性之外并不重要。
-
*
解引用(不立即引用结果)只有在你要解引用的引用的引用对象(&T
中的T
)实现了Copy
特性时才会编译。在这种情况下,可以保证结果是原始值的精确字节复制。 -
.clone()
,即Clone::clone()
,只有在引用的类型实现了Clone
特性时才会编译。在这种情况下,可以保证结果的类型与引用的类型相同:它从&T
变为T
。虽然没有编译器强制保证值是相同的,但_预期_Clone
实现将为你提供有意义的“相同值的另一个复制”,即使它的字节表示不同;例如,克隆一个Vec
将给你一个具有不同堆分配指针的Vec
,但内容将是原始向量的内容的克隆。所有实现
Copy
的类型也必须实现Clone
,你可以合理地期望这些类型使用任何方法都会产生相同的结果。(也就是说,如果Clone::clone()
的行为与复制不同,那么实现违反了文档中的期望。) -
.to_owned()
,即ToOwned::to_owned()
,只有在引用的类型实现了ToOwned
特性时才会编译。这个特性本质上与Clone
相同,不同之处在于结果类型不需要与引用类型相同;例如,在&str
上调用.to_owned()
将给你一个String
,而不是str
—— 这是很好的,因为str
不能 以值返回而没有一些包装。这就是为什么ToOwned
存在,与Clone
分开 — 以支持创建动态大小类型的克隆。所有实现
Clone
的类型都会通过全局实现自动实现ToOwned
。因此,以有趣的方式实现ToOwned
的唯一类型是那些_不_实现Clone
的类型;这些通常是动态大小的类型。
要编写良好的 Rust 代码:
- 如果可以使用解引用,请使用它。
- 如果不能使用解引用,请使用
.clone()
。 - 如果不能使用
.clone()
,请使用.to_owned()
。 - 如果以上都不起作用,请查阅类型的文档:
- 它可能具有专门的操作(例如
File::try_clone()
); - 它可能基本上是您无法制作克隆的东西,在这种情况下,您可能希望使用
Rc
或Arc
共享单个实例,或者您可能需要完全不同的操作;或者 - 代码的作者可能忘记提供
Copy
、Clone
或ToOwned
实现。
- 它可能具有专门的操作(例如
英文:
Assuming that all relevant traits are implemented sensibly, all three will have the same result, and should be equally efficient when compiled with optimization. The differences are in which ones are allowed under what conditions, and what they guarantee. Thus, if you are writing generic code (code that works with many different types) it may matter which one you pick, but if the code is not generic, it does not matter other than for readability.
-
*
dereference (without immediately referencing the result) will only compile if the referent of the reference you're dereferencing (theT
in&T
) implements theCopy
trait. In that case, it is guaranteed that the result is an exact byte-for-byte copy of the original value the reference pointed to. -
.clone()
, that is,Clone::clone()
, will only compile if the type of the referent implements theClone
trait. In that case, it is guaranteed that the result is of the same type as the referent: it goes from&T
toT
. There are no compiler-enforced guarantees that the value is identical, but it is expected thatClone
implementations will give you meaningfully “another copy of the same value”, even if its byte representation is different; for example, cloning aVec
will give you aVec
that has a different heap allocation pointer, but the contents will be a clone of the original vector's contents.All types that implement
Copy
must also implementClone
, and you can reasonably expect that those types will produce the same result from using either. (That is, ifClone::clone()
does something different from copying, the implementation is violating documented expectations.) -
.to_owned()
, that is,ToOwned::to_owned()
, will only compile if the type of the referent implements theToOwned
trait. This trait is essentially the same asClone
, except that the result type is not required to be the same as the referent type; for example, calling.to_owned()
on an&str
will give you aString
, not astr
— which is good, becausestr
cannot be returned by value without some wrapper. This is whyToOwned
exists distinct fromClone
— to support making clones of dynamically-sized types.All types that implement
Clone
automatically implementToOwned
via a blanket implementation. Thus, the only types that implementToOwned
in an interesting way are those which do not implementClone
; these are usually dynamically sized types.
To write good Rust code:
- If you can use a dereference, do that.
- If you cannot use a dereference, use
.clone()
. - If you cannot use
.clone()
, use.to_owned()
. - If none of those work, then consult the type's documentation:
- it may have a specialized operation (e.g.
File::try_clone()
); - it may be fundamentally something you can't make a clone of, in which case you may want to share the single instance with
Rc
orArc
, or you may need to do something completely different; or - the author of the code may have forgotten to provide a
Copy
,Clone
, orToOwned
implementation.
- it may have a specialized operation (e.g.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论