Rust Programming Book

imagemOlá Habitantes! O Official Rust Programming Language Guide o ajudará a criar um software mais rápido e confiável. Ergonomia de alto nível e controles de baixo nível freqüentemente entram em conflito um com o outro, mas Rust desafia esse conflito.



Os autores do livro fazem parte da equipe de desenvolvimento da linguagem, o que significa que você receberá todas as informações em primeira mão - desde a instalação da linguagem até a criação de programas confiáveis ​​e escaláveis. Da criação de funções, escolha de tipos de dados e variáveis ​​de vinculação, você passará para conceitos mais complexos:



  • Propriedade e empréstimo, ciclo de vida e tipos.
  • Segurança de software garantida.
  • Teste, tratamento de erros e refatoração eficaz.
  • Genéricos, ponteiros inteligentes, multithreading, objetos de característica e mapeamentos.
  • Trabalhe com o gerenciador de pacotes embutido Cargo para construir, testar, documentar código e gerenciar dependências.
  • Ferramentas avançadas para trabalhar com Unsafe Rust.


Você encontrará muitos exemplos de código, bem como três capítulos sobre a criação de projetos completos para solidificar o conhecimento: jogos de adivinhação, construção de uma ferramenta de linha de comando e um servidor multithread.



Para quem é este livro



Presumimos que você escreveu seu código em uma linguagem de programação diferente, mas não fazemos suposições sobre qual. Tentamos tornar este material acessível para aqueles com uma ampla gama de habilidades de programação. Não vamos perder tempo falando sobre o que é programação. Se você é um iniciante absoluto em programação, então primeiro leia a introdução à programação.



Como usar este livro
-, , , . , ; .



: . . , , . 2, 12 20 , — .



1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .



5 , 6 , match if let. Rust .



7 (API). 8 , , , -. 9 .



10 , , , . 11 , Rust . 12 grep, . , .



13 — , . 14 Cargo . 15 , , , .



16 , Rust . 17 Rust - , , , .



18 , Rust. 19 , , Rust, , , .

20 , !



, . Rust, Rust, , , , Rust.



: -, ! - , , , . , , .



Rust — , : . , , . , , ! , , , , . , .



Onde os padrões podem ser usados



Em Rust, os padrões aparecem em muitos lugares, e você os usou muito sem perceber! Esta seção discute situações em que os padrões são válidos.



Corresponder ramos de expressão



Conforme discutido no Capítulo 6, usamos padrões nos ramos das expressões de correspondência. Formalmente, as expressões de correspondência são definidas como correspondência de palavra-chave, o valor a ser correspondido e uma ou mais ramificações da correspondência consistindo no padrão e na expressão a ser executada se o valor corresponder ao padrão dessa ramificação, por exemplo:



   match  {
           => ,
           => ,
           => ,
   }


Um dos requisitos para expressões de correspondência é que elas devem ser abrangentes, no sentido de que todos os valores possíveis devem ser considerados em correspondência. Para que você considere todas as opções possíveis, você precisa ter um padrão abrangente no último ramo: por exemplo, um nome de variável que corresponda a qualquer valor sempre será acionado e, portanto, cobrirá todos os casos restantes.



O padrão especial _ corresponderá a qualquer coisa, mas não se vincula a uma variável e, portanto, é freqüentemente usado na última manga da correspondência. O padrão _ é útil, por exemplo, se você deseja ignorar qualquer valor não especificado. Consideraremos o padrão com mais detalhes na seção “Ignorando valores em um padrão”.



Se deixar condicionais



No Capítulo 6, discutimos as instruções if let principalmente como uma maneira mais curta de escrever o equivalente a uma expressão match que corresponda a apenas um caso. Alternativamente, if let pode ter um else correspondente contendo o código a ser executado se o padrão em if let não corresponder.



A Listagem 18.1 mostra que também é possível misturar e combinar as instruções if let, else if e else if let. Isso nos dá mais flexibilidade do que usar uma expressão de correspondência, que pode expressar apenas um valor para comparar com os padrões. Além disso, as condições em uma série de instruções if let, else if e else if let não precisam se referir umas às outras.



O código na Listagem 18.1 mostra uma série de testes para várias condições que decidem qual deve ser a cor de fundo. Neste exemplo, criamos variáveis ​​com valores embutidos em código que um programa real poderia recuperar da entrada do usuário.



Listagem 18.1. Misturando instruções if let, else if, else if let e else



src/main.rs
      fn main() {
             let favorite_color: Option<&str> = None;
             let is_tuesday = false;
             let age: Result<u8, _> = "34".parse();

      (1) if let Some(color) = favorite_color {
          (2) println!("   , {},   ", color);
      (3) } else if is_tuesday {
          (4) println!(" -  !");
      (5) } else if let Ok(age) = age {
          (6) if age > 30 {
               (7) println!("     ");
              } else {
               (8) println!("     ");
              }
      (9) } else {
          (10) println!("     ");
           }
     }


Se o usuário especificar uma cor favorita (1), esta é a cor de fundo (2). Se hoje for terça (3), a cor de fundo será verde (4). Se o usuário especificar sua idade como uma string e pudermos analisá-la com sucesso como um número (5), a cor será roxa (7) ou laranja (8), dependendo do valor do número (6). Se nenhuma dessas condições se aplicar (9), a cor de fundo é azul (10).



Essa estrutura condicional permite requisitos complexos. Com os valores codificados aqui, este exemplo produziria



     .


Você pode ver que uma expressão if let também pode introduzir variáveis ​​sombreadas da mesma forma que as mangas de uma expressão de correspondência: a linha de código if let Ok (idade) = idade (5) introduz uma nova variável sombreada idade que contém o valor dentro da variante Ok. Isso significa que precisamos colocar a condição se idade> 30 neste bloco (6): não podemos combinar essas duas condições na declaração se deixar Ok (idade) = idade && idade> 30. A variável sombreada idade que queremos comparar com 30 , será inválido até que o novo escopo comece com uma chave.



A desvantagem de usar instruções if let é que o compilador não verifica a exaustividade, mas sim as instruções match. Se tivéssemos pulado o último bloco else (9) e, portanto, o tratamento de alguns casos, o compilador não teria nos avisado sobre um possível erro lógico.



Enquanto deixa loops condicionais



Semelhante em design à instrução if let, o loop condicional while let permite que o loop while execute enquanto o padrão corresponder. O exemplo na Listagem 18.2 mostra um loop while let que usa um vetor como uma pilha e produz os valores no vetor na ordem inversa da ordem em que foram adicionados.



Listagem 18.2. Usando um loop while para imprimir valores enquanto stack.pop () retorna Some



    let mut stack = Vec::new();

    stack.push(1);
    stack.push(2);
    stack.push(3);

    while let Some(top) = stack.pop() {
          println!("{}", top);
    }


Este exemplo imprime 3, 2 e, em seguida, 1. O método pop pega o último elemento do Vetor e retorna Some (valor). Se o vetor estiver vazio, pop retornará Nenhum. O loop while continua executando o código em seu bloco até que pop retorne Some. Quando pop retorna None, o loop para. Podemos usar um loop condicional while, para remover cada item da pilha.



Para loops



No Capítulo 3, mencionamos que o loop for é a construção de loop mais comum no código Rust, mas não discutimos o padrão que leva ainda. Em um loop for, o padrão é o valor imediatamente após a palavra-chave for, portanto, em for x em y, o padrão é x.



A Listagem 18.3 mostra o uso de um padrão em um loop for para desestruturar ou decompor uma tupla dentro de um for.



Listagem 18.3. Usando um padrão em um loop for para desestruturar uma tupla



   let v = vec!['a', 'b', 'c'];

   for (index, value) in v.iter().enumerate() {
        println!("{}    {}", value, index);
   }


O código na Listagem 18.3 exibe o seguinte:



       0
   b    1
       2


Usamos o método enumerate para reescrever o iterador para produzir o valor e o índice desse valor no iterador, colocado em uma tupla. A primeira chamada ao método enumerate produz uma tupla (0, 'a'). Quando esse valor é combinado com o padrão (índice, valor), o índice é 0 e o valor é 'a', a primeira linha de dados é gerada.



Let declarações



Antes deste capítulo, discutimos diretamente o uso de padrões apenas com as instruções match e if let, mas na verdade usamos padrões em outros lugares, incluindo as instruções in let. Considere uma maneira simples de passar um valor de variável usando let:



let x = 5;


Usamos esse tipo de instrução let centenas de vezes ao longo deste livro e, embora você possa não ter percebido, está usando padrões! Mais formalmente, uma instrução let se parece com isto:



let  = ;


Em instruções como let x = 5; com um nome de variável no slot PATTERN, o nome da variável é apenas uma forma simples do padrão. Rust compara a expressão ao padrão e atribui quaisquer nomes que encontrar. Portanto, no exemplo, deixe x = 5; o padrão é x, o que significa "associar o que combina aqui com a variável x". Como o nome x representa o padrão inteiro, esse padrão significa efetivamente "vincular tudo à variável x, qualquer que seja o valor".



Para ver o mapeamento em relação ao padrão de instrução let com mais clareza, considere a Listagem 18.4, que usa o padrão let para desestruturar uma tupla.



Listagem 18.4. Usando um padrão para desestruturar uma tupla e criar três variáveis ​​de uma vez



let (x, y, z) = (1, 2, 3);


Aqui, estamos mapeando uma tupla para um padrão. Rust compara (1, 2, 3) com (x, y, z) e vê que esse valor corresponde ao padrão, então Rust associa 1 com x, 2 com y e 3 com z. Você pode pensar neste padrão de tupla como o aninhamento de três padrões de variáveis ​​separados nele.

Se o número de elementos no padrão não corresponder ao número de elementos na tupla, o tipo de agregação não corresponderá e receberemos um erro do compilador. Por exemplo, a Listagem 18.5 mostra uma tentativa de desestruturar uma tupla de três em duas variáveis, o que não funcionará.



Listagem 18.5. Construção incorreta do padrão, cujas variáveis ​​não coincidem com o número de elementos na tupla



let (x, y) = (1, 2, 3);


A tentativa de compilar este código resulta em um erro como:



error[E0308]: mismatched types
  --> src/main.rs:2:9
   |
2 |        let (x, y) = (1, 2, 3);
   |           ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
   |
   = note: expected type `({integer}, {integer}, {integer})`
                    found type `(_, _)`


Se quisermos ignorar um ou mais valores em uma tupla, poderíamos usar _ ou .., como você verá na seção “Ignorando valores em um padrão”. Se o problema for que há muitas variáveis ​​no padrão, você precisa fazer com que os tipos correspondam, excluindo as variáveis ​​de forma que o número de variáveis ​​seja igual ao número de elementos na tupla.



Parâmetros de função



Os parâmetros de função também podem ser padrões. O código na Listagem 18.6 que declara uma função foo que usa um parâmetro x do tipo i32 agora é familiar para você.



Listagem 18.6. A assinatura da função usa padrões em parâmetros



fn foo(x: i32) {
     //   
}


A parte x é um padrão! Como com let, podemos combinar a tupla nos argumentos da função com o padrão. A Listagem 18.7 divide os valores na tupla quando os passamos dentro da função.



Listagem 18.7. Função com parâmetros que desestruturam a tupla



src/main.rs
      fn print_coordinates(&(x, y): &(i32, i32)) {
            println!(" : ({}, {})", x, y);
      }

      fn main() {
            let point = (3, 5);
            print_coordinates(&point);
      }


Este código produz



 : (3, 5)


Os valores & (3, 5) correspondem ao padrão & (x, y), então x é 3 ey é 5.

Além disso, podemos usar padrões em listas de parâmetros de fechamento da mesma forma que em listas de parâmetros de funções, uma vez que fechamentos são semelhantes a funções, conforme descrito no Capítulo 13.



Você já viu várias maneiras de usar padrões, mas eles não funcionam da mesma forma em todos os lugares que você os usa. Em algumas situações, esses padrões devem ser irrefutáveis; em outras, eles podem ser refutáveis. Discutiremos esses dois conceitos a seguir.



Refutabilidade: a possibilidade de incompatibilidade de padrões



Existem dois tipos de padrões: refutáveis ​​e irrefutáveis. Os padrões que corresponderão a qualquer valor possível passado são irrefutáveis. Um exemplo é ox na instrução deixe x = 5; porque x corresponde a absolutamente tudo e, portanto, não pode deixar de corresponder. Os padrões que não correspondem a alguns dos significados possíveis são refutáveis. Um exemplo é Some (x) na instrução if let Some (x) = a_value, porque se o valor em a_value for None e não Some, então Some (x) não corresponderá.



Parâmetros de função, instruções let e loops for só podem aceitar padrões irrefutáveis ​​porque o programa não pode fazer nada significativo quando os valores não combinam. As expressões if let e while aceitam apenas padrões refutáveis, porque por definição elas são projetadas para lidar com um possível erro: a funcionalidade de uma expressão condicional é sua capacidade de executar ações diferentes dependendo do sucesso ou falha.



Em geral, você não deve se preocupar com a distinção entre padrões refutáveis ​​e irrefutáveis. No entanto, você ainda precisa estar ciente do conceito de refutabilidade para reagir ao vê-lo em uma mensagem de erro. Nesses casos, você precisará alterar o padrão ou a construção com a qual está usando o padrão, dependendo do comportamento pretendido do código.



Vamos dar uma olhada no que acontece quando tentamos usar um padrão irrefutável em um lugar onde Rust requer um padrão irrefutável e vice-versa. A Listagem 18.8 mostra uma instrução let, mas para o padrão especificamos Some (x), um padrão refutável. Como você pode esperar, este código não compila.



Listagem 18.8. Tentando usar um padrão refutável com let



let Some(x) = some_option_value;


Se alguma_opção_valor fosse igual a Nenhum, então não coincidiria com o padrão Some (x), ou seja, o padrão é refutável. No entanto, uma instrução let só pode aceitar um padrão irrefutável, uma vez que o código não pode fazer nada válido com o valor None. Em tempo de compilação, Rust reclamará que tentamos usar um padrão refutável onde um padrão irrefutável é necessário:



error[E0005]: refutable pattern in local binding: `None` not covered
  -->
   |
3 | let Some(x) = some_option_value;
   |     ^^^^^^^ pattern `None` not covered


Visto que não cobrimos (e não poderíamos ter) coberto todos os valores válidos com o padrão Some (x), Rust está corretamente lançando um erro do compilador.



Para corrigir o problema quando temos um padrão refutável em vez de irrefutável, podemos mudar o código que usa o padrão: em vez de let, podemos usar if let. Então, se o padrão não corresponder, o código entre chaves será ignorado e o trabalho continuará corretamente. A Listagem 18.9 mostra como corrigir o código da Listagem 18.8.



Listagem 18.9. Usando uma instrução if let e um bloco padrão refutável em vez de let



if let Some(x) = some_option_value {
    println!("{}", x);
}


O código está pronto! Este é um código absolutamente correto, embora signifique que não podemos usar um padrão irrefutável sem erro. Se dermos à expressão if let um padrão que sempre corresponderá, como x, conforme mostrado na Listagem 18-10, ela não será compilada.



Listagem 18.10. Tentando usar um padrão irrefutável com uma instrução if let



if let x = 5 {
    println!("{}", x);
};


O compilador reclama que não faz sentido usar uma expressão if let com um padrão irrefutável:



error[E0162]: irrefutable if-let pattern
  --> <anon>:2:8
   |
2 | if let x = 5 {
   |        ^ irrefutable pattern


Por esse motivo, as luvas da expressão de correspondência devem usar padrões refutáveis, com exceção da última luva, que deve corresponder a quaisquer valores restantes em relação ao padrão irrefutável. Rust permite que o padrão irrefutável seja usado em uma expressão de correspondência com apenas uma manga, mas essa sintaxe não é particularmente útil e pode ser substituída por uma instrução let mais simples.



Agora que você sabe onde os padrões são usados ​​e como os padrões refutáveis ​​e irrefutáveis ​​diferem, vamos nos familiarizar com a sintaxe que podemos usar para criar padrões.



Sobre os autores



Steve Klabnik lidera a equipe de documentação do Rust e é um dos principais desenvolvedores da linguagem. Ele é um palestrante frequente e escreve muito código-fonte aberto. Anteriormente trabalhou em projetos como Ruby e Ruby on Rails.



Carol Nichols é membro da equipe de desenvolvimento Rust Core e cofundadora da Integer 32, LLC, a primeira empresa de consultoria de desenvolvimento de software com foco em Rust do mundo. Nichols é o organizador da conferência Rust Belt sobre a linguagem Rust.



»Mais detalhes sobre o livro podem ser encontrados no site da editora

» Índice

» Trecho



Para Habitans desconto de 25% no cupom - Ferrugem No ato do



pagamento da versão em papel do livro, é enviado um e-book para o e-mail.



All Articles