Aprender sobre esses dois conceitos ajudará a reforçar sua compreensão de como o Rust funciona e como você pode implementar recursos OOP.
Características e tempos de vida são componentes-chave do Rust. Você pode usar características para definir comportamentos e recursos para tipos a serem implementados. Eles são muito versáteis, permitindo que você escreva um código mais genérico, reduza a duplicação e melhore a capacidade de manutenção.
Rust usa outro mecanismo - tempos de vida - para rastrear a propriedade de variáveis dentro e fora do escopo. Isso evita ponteiros oscilantes durante a desalocação de variáveis.
Juntos, características e tempos de vida ajudam a garantir segurança de tipo, segurança de memória e confiabilidade de código.
Compreendendo as características da ferrugem
Características são coleções de métodos que outros tipos podem implementar. Os traços são semelhantes aos interfaces em linguagens como Java, Go e TypeScript, mas mais flexíveis.
Você usará o característica palavra-chave para definir características em Rust, seguida por uma declaração de assinaturas de método.
característicaMyTrait {
fnmeu_método(&auto);
}
O código define uma característica chamada MyTrait com um meu_método método. O &auto O parâmetro indica que o método se refere ao objeto do tipo de implementação como seu primeiro parâmetro.
Depois de definir uma característica, você pode implementá-la para seus tipos personalizados.
Veja como você pode implementar uma característica para seus tipos de struct.
estruturaPessoa {
nome: Corda,
idade: u32,
}
implicar Informações para Pessoa {
fnresumo(&auto) {
println!("Meu nome é {} e tenho {} anos.", auto.nome, auto.idade);
}
}
O Pessoa implementos de estrutura Informações, e você pode ligar para o resumo método em instâncias do Pessoa struct.
fnprincipal(){
deixar joão = Pessoa { nome: Corda::de("John"), idade: 30 };
john.summary(); // Saída: Meu nome é John e tenho 30 anos.
}
O John variável é uma instância do Pessoa struct.
O principal chamadas de função resumo que imprime uma mensagem para o console:
Enums podem implementar características. Veja como você pode definir uma enumeração com variantes que implementam o resumo método:
enumerarMyEnum {
Variante A,
Variante B,
}
implicar Informações para MeuEnum {
fnresumo(&auto) {
corresponderauto {
MyEnum:: VariantA => {
// implementação para VariantA
}
MyEnum:: VariantB => {
// implementação para VariantB
}
}
}
}
Usando características para parâmetros de função e valores de retorno
Você pode usar características como parâmetros de função e valores de retorno. Usar traços como parâmetros de função é útil para escrever código genérico com vários tipos.
Aqui está uma função que recebe um parâmetro de qualquer tipo que implementa Informações.
fnfaça alguma coisa(valor: T) {
value.summary();
}
O a sintaxe especifica que T deve implementar Informações. Você pode ligar para o resumo função com qualquer valor que implemente Informações.
Vidas em ferrugem
A ferramenta de verificação de empréstimo do Rust analisa programas e garante o uso adequado da memória. Em Rust, todo valor tem um dono que é responsável por desalocar o valor. Quando variáveis emprestam valores, eles emprestam uma referência ao valor passado, mas o proprietário mantém a propriedade.
Os tempos de vida são uma forma de garantir que os valores emprestados sejam usados corretamente. Um tempo de vida é um rótulo anexado a uma referência, descrevendo por quanto tempo a referência é válida.
No Rust, você pode especificar um tempo de vida usando uma anotação de apóstrofo:
função <'a>
Ao criar uma referência, a referência recebe um tempo de vida que descreve por quanto tempo ela é válida. Se você tiver uma função que usa a referência a um valor, o tempo de vida deve ser maior que a chamada da função para garantir que o valor seja válido quando a função retornar.
Aqui está um exemplo de especificação de tempo de vida em uma função.
fnfaça alguma coisa<'a>(x: &'ai32) -> &'ai32 {
x
}
fnprincipal() {
deixar x = 42;
deixar resultado = fazer_algo(&x);
println!("O resultado é: {}", resultado);
}
No faça alguma coisa função, o 'a parâmetro de tempo de vida indica que a referência a x é válido enquanto a chamada da função. A referência retornada também é válida desde que a chamada da função.
O principal A função imprime o resultado passando uma referência ao x variável no principal função para o console.
A sintaxe do tempo de vida pode ser detalhada, mas é essencial para segurança e gerenciamento de memória. As regras de elisão de três tempos de vida fornecem diretrizes que permitem ao Rust inferir o tempo de vida de referências em determinadas situações.
A regra de tempo de vida de entrada
A regra de tempo de vida de entrada especifica que, se uma função ou método usa uma ou mais referências como parâmetros de entrada, o Rust assume que todas as referências têm o mesmo tempo de vida.
Simplificando, o tempo de vida das referências de saída será o mesmo das referências de entrada.
fnmais longo<'a>(x: &'astr, y: &'astr) -> &'astr {
se x.len() > y.len() { x } outro {s}
}
No mais longo função, Rust infere que o tempo de vida da referência de saída é o mesmo que a referência de entrada porque ambos têm o mesmo parâmetro de tempo de vida 'a.
A regra de tempo de vida de entrada facilita a escrita de funções genéricas que aceitam várias referências como entrada.
A regra de tempo de vida de saída
A regra de tempo de vida de saída especifica que, se uma função ou método retornar uma referência, o Rust assumirá que o tempo de vida da referência de saída é diferente do tempo de vida de qualquer referência de entrada.
fnprimeira palavra<'a>(s: &'astr) -> &'astr {
s.split_whitespace().next().unwrap()
}
Nesta função, Rust infere que o tempo de vida da referência de saída é diferente do tempo de vida da referência de entrada porque o split_whitespace() O método cria uma referência de saída que não aceita parâmetros de referência de entrada.
A Regra de Elisão de Tempos de Vida
A regra de elisão de tempos de vida se aplica se uma função ou método usa uma referência ou parâmetro de entrada e retorna uma referência. Nesse caso, Rust assume que a referência de saída tem o mesmo tempo de vida que a referência de entrada.
fnmais longo<'a>(x: &'astr, y: &str) -> &'astr {
se x.len() > y.len() { x } outro {s}
}
Nesta função, Rust infere que o tempo de vida da referência de saída é o mesmo que o tempo de vida da referência de entrada porque a referência de entrada y não tem parâmetro de tempo de vida. Rust elimina o parâmetro de tempo de vida para y e assume que tem o mesmo tempo de vida que x.
Essa regra torna mais fácil escrever funções que usam uma referência de entrada e retornam uma referência de saída.
Traços e tempos de vida
Você pode combinar características e tempos de vida para criar funções genéricas que funcionam para tipos que implementam uma característica e têm um tempo de vida válido.
Aqui está um traço e uma função que faz referência a um valor que implementa o traço.
característicaPara sequenciar {
fnpara sequenciar(&auto) -> Corda;
}
fnpara sequenciar<'a, T: Para sequenciar>(t: &'a V) -> Corda {
t.to_string()
}
Aqui, o parâmetro de tempo de vida 'a garante que a referência t é válido para o tempo de vida do objeto que ele referencia. Você pode usar o para sequenciar função com tipos que implementam o Para sequenciar característica com um tempo de vida válido.
As características formam a base para a implementação de conceitos OOP em Rust
Traços permitem que você defina comportamentos. Embora Rust não seja uma linguagem de programação orientada a objetos (OOP), você pode usar traits para implementar conceitos OOP de encapsulamento a herança, polimorfismo e abstração.
A implementação desses conceitos OOP com características torna seus programas Rust escaláveis, robustos, sustentáveis e eficientes.