Ir para o conteúdo

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:

talor
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 needed

Um 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:

talor
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 needed

Deep 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.

talor
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.

Talor v0.1.0 - Publicado sob a licença MIT OR Apache-2.0.