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.