Contents

Rust - Ownership - Parte 2

Contents

Ownership - Transferência entre variáveis

Já sabemos que quando chamamos uma função em RUST e passamos uma variável de memória, que possuí seu conteúdo na heap, por parâmetro o que estamos fazendo é transferindo a propriedade (‘owner’) dessa variável para a nova função e ao mesmo tempo invalidando o uso daquela variável na função original.

Mas como funciona quando estamos copiando dados entre variáveis alocadas na heap dentro da mesma função?

1
2
3
4
fn main() {
    let s1 = String::from("String alocada na heap"); // Linha 1
    let s2 = s1;                                     // Linha 2
}

No exemplo acima, quando chegamos na ‘Linha 2’ RUST transfere ‘owner’ de s1 para s2 e como esperado, invalida s1. Se tentarmos acessar s1 após a ‘Linha 2’ vamos receber um erro do compilador dizendo que s1 foi “movida” para s2 e que s1 não é mais válido.

Isso acontece para evitar problemas que são comuns em C como ‘double free’. Quando o código fica muito longo podemos nos perder e acabar esquecendo que já desalocamos uma área de memória e se tentamos desalocar essa área de memória outra vez nosso programa falha pois, naquele momento, estamos acessando uma área de memória já invalida para o nosso programa.

Você lembra que o RUST desaloca toda a memória, inclusive a alocada na heap, quando o escopo de uma função chega ao seu fim? Com isso em mente pense no que poderia acontecer se o ‘owner’ não tivesse sido movido e s1 invalidado. Nesse caso RUST daria, automaticamente, free em s1 e como não teria como saber que s2 estava apontando para a mesma posição de memória que s1 ele ira dar free em s1 também. Pronto, ‘double free’ :(. Mas graças a transferência de ‘owner’ isso não acontece.

Tipo de variáveis e alocação em stack

É bom deixar claro que tipos de variáveis são alocadas na ‘stack’ e que tipo de variáveis são alocadas na ‘heap’

Variáveis que serão alocadas na stack:

  • Todos os tipos de inteiros: i32, u32, i64, u64
  • Bonelanos: bool
  • Pontos flutuantes: f32, f64
  • Caracters: char
  • Tuples que contem SOMENTE os tipos listados acima

Esses tipos acima são alocados na stack pois todos eles possuem tamanhos já conhecidos pelo compilador e portanto são passíveis de viver dentro da stack e para tais o conceito de transferência de ‘owner’ não existe.

1
2
3
4
fn main() {
    let i1 = 6;  // Linha 1
    let i2 = i1; // Linha 2
}

No exemplo acima, quando chegamos em ‘Linha 2’ a variável ‘i1’ não sera invalidada pois ‘i2’ é uma variável nova que aponta para um novo endereço de memória, o qual possuí o mesmo valor que ‘s1’ devido a atribuição. Ao fim da função, quando as variáveis ‘s1’ e ‘s2’ saem do escopo RUST pode desalocar a memória das duas sem problema pois nenhuma das duas aponta para a mesma região de memória.