有差异吗 .to_owned()、.clone() 和解引用 (*) 之间?

huangapple go评论76阅读模式
英文:

Are there differences between .to_owned(), .clone() and dereferencing (*)?

问题

使用这个在 Rust Playground 上的代码

我看到:

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类型:

  1. *ref_to_val返回val的位拷贝(不使旧值无效)。
  2. 对于Copy类型,派生的Clone实现将使用*来实现.clone()
  3. 对于Clone类型,通用的ToOwned实现将使用.clone()来实现.to_owned()

所以最终都归结为位拷贝。
这是你的情况。

如果你想了解特质DerefCloneToOwned如何用于!Copy类型,你需要了解它们的目的并阅读特定于类型的文档。
我认为Stringstr类型是非常典型的!Copy类型,它们将帮助你理解这种魔法。例如,strToOwned,但不是DerefClone。你的示例对于这些类型不起作用。

英文:

For a reference to an instance of Copy there is no difference.
That is because for Copy types:

  1. *ref_to_val returns a bitwise copy of val (and doesn't invalidate old value).
  2. The derived Clone impl. for Copy types will implement .clone() using *.
  3. The blanket ToOwned impl. for Clone 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());
    • 它可能基本上是您无法制作克隆的东西,在这种情况下,您可能希望使用 RcArc 共享单个实例,或者您可能需要完全不同的操作;或者
    • 代码的作者可能忘记提供 CopyCloneToOwned 实现。
英文:

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 (the T in &T) implements the Copy 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 the Clone trait. In that case, it is guaranteed that the result is of the same type as the referent: it goes from &T to T. There are no compiler-enforced guarantees that the value is identical, but it is expected that Clone implementations will give you meaningfully “another copy of the same value”, even if its byte representation is different; for example, cloning a Vec will give you a Vec 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 implement Clone, and you can reasonably expect that those types will produce the same result from using either. (That is, if Clone::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 the ToOwned trait. This trait is essentially the same as Clone, 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 a String, not a str — which is good, because str cannot be returned by value without some wrapper. This is why ToOwned exists distinct from Clone — to support making clones of dynamically-sized types.

    All types that implement Clone automatically implement ToOwned via a blanket implementation. Thus, the only types that implement ToOwned in an interesting way are those which do not implement Clone; 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 or Arc, or you may need to do something completely different; or
    • the author of the code may have forgotten to provide a Copy, Clone, or ToOwned implementation.

huangapple
  • 本文由 发表于 2023年7月27日 19:07:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76779130.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定