Estou lendo "o livro" e queria testar minha compreensão das regras de empréstimo no fatiamento de capítulos .
Fiquei impressionado com (minha suposição de) como o verificador de empréstimos associava o que eu passava, com o que a função retornava. Então, tentei alguns exemplos para testar meu entendimento, e estou com dificuldades para descobrir as regras do verificador de empréstimos.
- Por que
test1
falha ao compilar, ondetest5
funciona? - Onde meu entendimento está quebrado? (veja o processo de pensamento abaixo)
- Onde "o livro" aborda o que estou perdendo?
Este código é sempre o mesmo:
fn tmp() {
let mut s = String::from("hello world");
let thing = testX(&s);
s.clear(); // error!
let tmp = thing.len();
println!("the first word is: {tmp}");
}
Teste 1:
fn test1(s: &String) -> &str {
"Slice"
}
Aqui estou passando uma referência imutável de uma string mutável, mas não a uso na função. Eu retorno uma fatia diferente (na minha mente, uma string imutável diferente). Acredito que uma vez test1
retornado ele não mantém mais seu empréstimo em &s
. Eu esperava que esse código compilasse, mas não compila, eu ainda mantenho de alguma forma um empréstimo imutável em s
.
Então eu me perguntei se o verificador de empréstimo correlaciona o tipo retornado com o tipo que foi passado.
Teste 2:
fn test2(s: &String) -> Vec<&str> {
let mut vec = Vec::new();
vec.push(s.as_str());
vec
}
Tentei ser criativo com mais indireção. Não tenho ideia do que as_str()
acontece em termos de propriedade, mas estou interessado em aprender. Aqui estou passando de volta uma parte do que foi passado, então não estou surpreso que não compile, mas já suspeito que meu entendimento das regras esteja errado.
Teste 3
fn test3(s: &String) -> Vec<&str> {
let mut vec = Vec::new();
vec.push("Slice");
vec
}
Eu sigo um raciocínio similar ao test1
, também não compila. Isso me diz que não se trata de retornar um tipo de referência do tipo que foi passado, a menos que esteja estendendo o raciocínio para o tipo do vetor proprietário..?
Teste 4
fn test4(s: &String) -> Vec<&usize> {
let mut vec = Vec::new();
vec.push(&1);
vec
}
Agora estou ficando furtivo, estou criando um código que acho que é comparável a test3
, mas retorna um tipo não relacionado. Ainda assim, o compilador não permite. Então parece que por algum motivo a função está segurando o empréstimo de &String
depois que ele retorna?!
Teste 5
fn test5(s: &String) -> Vec<String> {
let mut vec = Vec::new();
vec.push(String::from("something new"));
vec
}
Eu tentei isso, esperando que também não compilasse, mas funcionou!
Eu vejo isso como logicamente o mesmo que test1
em relação ao empréstimo de s
. Em ambos os casos, não estou fazendo nada com s
, mas ainda assim test1
mantenho o empréstimo, enquanto test5
não.
Ajuda!
Vou tentar o teste 1 primeiro — espero que isso lhe dê algumas dicas sobre os outros casos também.
Quando você faz:
... há um especificador de tempo de vida implícito sendo definido, e é o mesmo para ambos os empréstimos. É como se você tivesse escrito:
Isso significa que, até onde o chamador
test1
sabe, os tempos de vida são os mesmos, e&str
poderia reter alguns dados que o chamador&String
tinha. Na verdade, isso também compilaria:Da perspectiva do chamador, ele não sabe o que está acontecendo no corpo, e então o verificador de empréstimo tem que "assumir o pior", por assim dizer — isto é, que o corpo é algo como aquele último trecho. Agora você pode ver por que
s.clear()
é um erro: você está limpando a memória subjacente quething
depende de!Você pode consertar isso fornecendo especificadores de tempo de vida explícitos. Qualquer um destes funcionará:
A maioria dos seus outros testX são basicamente variantes do tema: você está dizendo ao compilador que o valor de retorno está, de alguma forma, vinculado ao tempo de vida da entrada.
test5 é a exceção: o valor de retorno não se refere ao tempo de vida de empréstimo da entrada, mas, em vez disso, é sua própria entidade totalmente de propriedade.
String::from
copiou os dados para uma nova String (em vez de referenciar os dados na string original), e o fato de não haver empréstimo formaliza isso para o sistema de tipos. Com isso, o sistema de tipos diz "ah, ninguém mais está contando coms
, então estou livre para permitir modificações nele."