Nota do Editor : esta pergunta foi feita antes do Rust 1.0 e algumas das afirmações na pergunta não são necessariamente verdadeiras no Rust 1.0. Algumas respostas foram atualizadas para atender às duas versões.
Eu tenho esta estrutura
struct Triplet {
one: i32,
two: i32,
three: i32,
}
Se eu passar isso para uma função, ele será copiado implicitamente. Agora, às vezes eu leio que alguns valores não são copiáveis e, portanto, precisam ser movidos.
Seria possível tornar esta estrutura Triplet
não copiável? Por exemplo, seria possível implementar um traço que tornasse Triplet
não copiável e, portanto, "móvel"?
Eu li em algum lugar que é preciso implementar a Clone
característica de copiar coisas que não são copiáveis implicitamente, mas nunca li sobre o contrário, que é ter algo que é implicitamente copiável e torná-lo não copiável para que seja movido em seu lugar.
Isso faz algum sentido?
Respostas:
Prefácio : Esta resposta foi escrita antes que as características integradas opt-in - especificamente os
Copy
aspectos - fossem implementadas. Usei aspas em bloco para indicar as seções que se aplicavam apenas ao esquema antigo (aquele que se aplicava quando a pergunta foi feita).Os tipos agora se movem por padrão, ou seja, quando você define um novo tipo, ele não implementa, a
Copy
menos que você o implemente explicitamente para o seu tipo:A implementação só pode existir se cada tipo contido no novo
struct
ouenum
for ele mesmoCopy
. Caso contrário, o compilador imprimirá uma mensagem de erro. Ele também só pode existir se o tipo não tiver umaDrop
implementação.Para responder à pergunta que você não fez ... "o que há com moves e copy?":
Em primeiro lugar, definirei duas "cópias" diferentes:
(&usize, u64)
, é de 16 bytes em um computador de 64 bits, e uma cópia superficial tomaria esses 16 bytes e replicaria seus valor em algum outro pedaço de memória de 16 bytes, sem tocarusize
no na outra extremidade do&
. Ou seja, é equivalente a chamarmemcpy
.Rc<T>
envolve apenas o aumento da contagem de referência, e uma cópia semântica de umVec<T>
envolve a criação de uma nova alocação e, em seguida, a cópia semântica de cada elemento armazenado do antigo para o novo. Podem ser cópias profundas (por exemploVec<T>
) ou rasas (por exemploRc<T>
, não toca no armazenadoT
),Clone
é definido vagamente como a menor quantidade de trabalho necessária para copiar semanticamente um valor do tipoT
de dentro de&T
paraT
.Rust é como C, cada uso por valor de um valor é uma cópia de byte:
Eles são cópias de bytes, sejam ou não
T
movidos ou ou "implicitamente copiáveis". (Para ser claro, eles não são necessariamente cópias literalmente byte a byte em tempo de execução: o compilador é livre para otimizar as cópias se o comportamento do código for preservado.)No entanto, há um problema fundamental com cópias de byte: você acaba com valores duplicados na memória, o que pode ser muito ruim se eles tiverem destruidores, por exemplo
Se
w
fosse apenas uma cópia simples dev
então haveria dois vetores apontando para a mesma alocação, ambos com destruidores que o libertam ... causando um duplo livre , o que é um problema. NB. Isso seria perfeitamente normal, se fizéssemos uma cópia semântica dov
emw
, pois entãow
seria independenteVec<u8>
e os destruidores não estariam atropelando uns aos outros.Existem algumas soluções possíveis aqui:
w
tenha sua própria alocação, como C ++ com seus construtores de cópia.v
não pode mais ser usado e não tem seu destruidor rodando.O último é o que o Rust faz: um movimento é apenas um uso por valor em que a fonte é invalidada estaticamente, de modo que o compilador evita o uso futuro da memória agora inválida.
Tipos que têm destruidores devem se mover quando usados por valor (também conhecido como quando byte copiado), uma vez que eles têm gerenciamento / propriedade de algum recurso (por exemplo, uma alocação de memória ou um identificador de arquivo) e é muito improvável que uma cópia de byte duplique isso corretamente propriedade.
"Bem ... o que é uma cópia implícita?"
Pense em um tipo primitivo como
u8
: uma cópia de byte é simples, basta copiar o byte único, e uma cópia semântica é tão simples, copiar o byte único. Em particular, uma cópia de byte é uma cópia semântica ... Rust ainda tem um traço embutidoCopy
que captura quais tipos têm cópias semânticas e de byte idênticas.Portanto, para esses
Copy
tipos, os usos por valor também são cópias semânticas automaticamente e, portanto, é perfeitamente seguro continuar usando o código-fonte.Conforme mencionado acima, características integradas opt-in são implementadas, portanto, o compilador não tem mais comportamento automático. No entanto, a regra usada para o comportamento automático no passado são as mesmas regras para verificar se é legal implementar
Copy
.fonte
A maneira mais fácil é incorporar algo no seu tipo que não possa ser copiado.
A biblioteca padrão fornece um "tipo de marcador" exatamente para este caso de uso: NoCopy . Por exemplo:
fonte