Ownership
O Talor mantém a garantia do Rust e abandona a cerimônia dele. Todo tipo que possui dados no heap é liberado pelo compilador, toda cópia ou é gratuita ou é escrita, e nada é contado pelas suas costas. Não existe mut x: T, nem own x, nem marcador no ponto em que um valor passa por move.
Moves
Um valor que não é plain passa por move quando é entregue: guardado em uma struct, em uma variant, em um array ou em uma tupla, devolvido, passado a um parâmetro que fica com ele, ou capturado por uma closure. Usá-lo depois é um erro de compilação:
let a = [1, 2, 3];
let all: Array<Array<i64>> = [];
let b = a;
all.push(b); // b is handed over, so the `let` moved a
a.len() // error: 'a' was moved on line 3; write 'a.clone()' at the move if both uses are neededUm let cujo novo nome só é lido não faz move: let b = a; seguido de leituras de b e de a é aceito, porque os dois nomes leem o mesmo array. O let faz o move do valor quando o novo nome é entregue ou recebe uma escrita.
O compilador trata o último uso de uma variável como um move automaticamente, então xs.push(item) faz o move de item quando nada o lê depois, e nenhum clone é necessário.
Um campo que saiu
Um campo pode sair de uma struct por move sozinho. A struct não pode ser usada como um todo enquanto o campo está vazio, mas os outros campos podem, e uma escrita no campo o preenche de novo:
struct Bag { items: Array<i64>, n: i64 }
fn eat(xs: Array<i64>): i64 takes xs { xs.len() }
fn main(): i32 {
let b = Bag { items: [1, 2, 3], n: 0 };
let k = eat(b.items); // `items` moves out of `b`
b.n = 5; // a field beside it: accepted
b.items = [5, 6]; // the field is filled again, and `b` is whole
println(`${k} ${b.items.len()} ${b.n}`);
0
}Ler o campo entre o move e a escrita é recusado.
A regra do clone
O compilador copia implicitamente só tipos plain. Para qualquer outro tipo, quando um valor é necessário duas vezes o programa precisa dizer o que quer, e o erro diz onde:
error: 'tokens' was moved on line 4; write 'tokens.clone()' at the move if both uses are neededDeep copies silenciosas e reference counting silencioso nunca acontecem. Compartilhar é um tipo: shared T.
.clone() é uma deep copy: um array copia seus elementos, um Box aloca um novo box, e um shared soma um à sua contagem.
fn main(): i32 {
let b = Box.new([1, 2]);
let c = b.clone(); // Box<Array<i64>>: a second box, a second array
let xs = (*b).clone(); // Array<i64>: a copy of the array inside
xs.push(3);
println(`${(*c).len()} ${xs.len()} ${(*b).len()}`);
0
}Dois tipos de valor não aceitam clone, nem nada que guarde um deles:
- Uma closure, porque os valores que ela capturou têm um único dono.
- Um valor que guarda um recurso, como um arquivo, um socket ou um handle de thread, porque o runtime libera um recurso uma vez só, e duas cópias o liberariam duas vezes.
Quando dois detentores precisam de um desses, coloque-o em um shared e clone esse: o clone é uma contagem, e os dois detentores alcançam o mesmo valor.
Drop
Valores possuídos são liberados no fim do scope que os possui, em todo caminho de saída (return, break, continue, ?) e no caminho de panic, em que o unwind executa as mesmas liberações. Uma struct libera seus campos na ordem de declaração; uma variant libera o payload do caso que ela guarda. Um valor que passou por move não é liberado pelo dono anterior.
Um temporário, um valor sem nome, é liberado no fim do statement que o criou, ou no fim do branch em que foi criado. A única exceção é o receiver de um método que fica com ele: o método o possui a partir da chamada.
Ler não é tomar
O que uma função faz além de ler, o compilador lê do corpo dela, e o call site não escreve nada: borrowing cobre os parâmetros, a clause edits e takes, e quando uma chamada faz uma cópia de um argumento por você.
Uma leitura de um lugar que a função não possui - um elemento, um campo de um valor em borrow, uma variável de loop - mantém o borrow em vez de copiar o valor, e a região em que esse borrow vive é inferida do fluxo de controle: lifetimes.