Esse problema parece sugerir que é apenas um detalhe da implementação ( memcpy
vs ???), mas não consigo encontrar nenhuma descrição explícita das diferenças.
Esse problema parece sugerir que é apenas um detalhe da implementação ( memcpy
vs ???), mas não consigo encontrar nenhuma descrição explícita das diferenças.
Respostas:
Clone
foi projetado para duplicações arbitrárias: uma Clone
implementação para um tipo T
pode executar operações arbitrariamente complicadas, necessárias para criar um novoT
. É uma característica normal (além de estar no prelúdio) e, portanto, exige ser usada como uma característica normal, com chamadas de método etc.
A Copy
característica representa valores que podem ser duplicados com segurança via memcpy
: coisas como reatribuições e passar um argumento por valor para uma função são sempre memcpy
s, e, para os Copy
tipos, o compilador entende que não precisa considerar essas mudanças .
Clone
é uma cópia em profundidade e é uma cópia em Copy
sombra?
Clone
abre a possibilidade de que o tipo possa fazer uma cópia profunda ou superficial: "arbitrariamente complicado".
A principal diferença é que a clonagem é explícita. Notação implícita significa mover para um não- Copy
tipo.
// u8 implements Copy
let x: u8 = 123;
let y = x;
// x can still be used
println!("x={}, y={}", x, y);
// Vec<u8> implements Clone, but not Copy
let v: Vec<u8> = vec![1, 2, 3];
let w = v.clone();
//let w = v // This would *move* the value, rendering v unusable.
A propósito, todo Copy
tipo também é necessário Clone
. No entanto, eles não são obrigados a fazer a mesma coisa! Para seus próprios tipos, .clone()
pode ser um método arbitrário de sua escolha, enquanto a cópia implícita sempre acionará uma memcpy
, não a clone(&self)
implementação.
y
ser movido x
, e não uma cópia, como no seu último exemplo comentado w = v
. Como você especificaria isso?
Copy
deve ser implementado para tipos "baratos", como u8
no exemplo. Se você escreve um tipo bastante pesado, para o qual considera que uma movimentação é mais eficiente que uma cópia, não implemente Copy
. Observe que, no caso do u8, você não pode ser mais eficiente com um movimento, pois, por baixo do capô, isso provavelmente implicaria pelo menos uma cópia indicadora - que já é tão cara quanto uma cópia do u8, então, por que se preocupar?
Copy
característica afeta os escopos implícitos da vida útil das variáveis? Se sim, acho isso digno de nota.
Como já coberto por outras respostas:
Copy
é implícito, barato e não pode ser reimplementado (memcpy).Clone
é explícito, pode ser caro e pode ser reimplementado arbitrariamente.O que às vezes falta na discussão de Copy
vs Clone
é que ele também afeta como o compilador usa movimentos versus cópias automáticas. Por exemplo:
#[derive(Debug, Clone, Copy)]
pub struct PointCloneAndCopy {
pub x: f64,
}
#[derive(Debug, Clone)]
pub struct PointCloneOnly {
pub x: f64,
}
fn test_copy_and_clone() {
let p1 = PointCloneAndCopy { x: 0. };
let p2 = p1; // because type has `Copy`, it gets copied automatically.
println!("{:?} {:?}", p1, p2);
}
fn test_clone_only() {
let p1 = PointCloneOnly { x: 0. };
let p2 = p1; // because type has no `Copy`, this is a move instead.
println!("{:?} {:?}", p1, p2);
}
O primeiro exemplo ( PointCloneAndCopy
) funciona bem aqui por causa da cópia implícita, mas o segundo exemplo ( PointCloneOnly
) com erro após o uso após a movimentação:
error[E0382]: borrow of moved value: `p1`
--> src/lib.rs:20:27
|
18 | let p1 = PointCloneOnly { x: 0. };
| -- move occurs because `p1` has type `PointCloneOnly`, which does not implement the `Copy` trait
19 | let p2 = p1;
| -- value moved here
20 | println!("{:?} {:?}", p1, p2);
| ^^ value borrowed here after move
Para evitar a mudança implícita, poderíamos ligar explicitamente let p2 = p1.clone();
.
Isso pode levantar a questão de como forçar uma movimentação de um tipo que implementa a característica de cópia? . Resposta curta: você não pode / não faz sentido.