├── 08 - Packages, Crates e Módulos ├── 01 - Começo.md ├── 02 - Packages e Crates.md └── 03 - Definindo Módulos para Controlar Escopo e Privacidade.md ├── 10 - Lidando Com Erro ├── 01 - Começo.md ├── 02 - Erros Irrecuperáveis com Panic!.md └── 03 - Erros Recuperáveis com Result.md ├── 01 - Introdução.md ├── 11 - Genéricos, Traits e Lifetime ├── 03 - Validando Referências com Lifetime.md ├── 02 - Traits, Definindo Comportamento Compartilhado.md └── 01 - Tipos de Dados Genéricos.md ├── 09 - Coleções Comuns ├── 01 - Guardando Lista de Valores com Vetores.md ├── 03 - Armazenando Keys com Valores Associados em Hash Maps.md └── 02 - Armazenando Texto UTF-8 com Strings.md ├── 04 - Conceitos comuns ├── 01 - Variáveis e Mutabilidade.md ├── 03 - Funções.md ├── 02 - Tipos de Data.md └── 04 - Controle de Fluxo.md ├── 02 - Começando ├── 01 - Hello, World!.md └── 02 - Hello, Cargo!.md ├── 12 - Testes └── 01 - Como Escrever Testes.md ├── 06 - Structs ├── 02 - Métodos Syntax.md └── 01 - Definindo e Instanciando Estruturas.md ├── README.md ├── 07 - Enumeradores ├── 02 - Construção de Controle de Fluxo.md └── 01 - Enumeradores e Pattern Matching.md ├── 05 - Entendendo Ownership ├── 02 - Referências e Empréstimos.md └── 01 - O Que é Ownership.md └── 03 - Jogo de adivinhação └── 01 - Jogo.md /08 - Packages, Crates e Módulos/01 - Começo.md: -------------------------------------------------------------------------------- 1 | # Começo 2 | 3 | Quando estiver escrevendo programas grandes, organizar seu código será muito importante, agrupando funcionalidades e separando o código com características distintas você vai entender melhor na hora de arrumar um bug ou implementar uma nova feature. 4 | 5 | O rust tem inúmeras features que te permitem manusear a organização do seu código, incluindo quais detalhes são públicos e privados, algumas includem: 6 | 7 | - Packages: Feature do cargo que deixa você criar, testar e compartilhar crates; 8 | - Crates: Árvore de módulos que produzem uma biblioteca ou um executável; 9 | - Modulos e use: Deixam você controlar a organização, escopo e privacidade dos caminhos; 10 | - Paths: Uma maneira de nomas um item, como uma struct, function ou module. 11 | 12 | 13 | -------------------------------------------------------------------------------- /10 - Lidando Com Erro/01 - Começo.md: -------------------------------------------------------------------------------- 1 | 2 | Erros são um fato de vida em um software, então o Rust possuí inúmeras formas de lidar quando algo da errado. Na maioria dos casos, o Rust quer que você tenha conhecimento da possibilidade de um erro e faça algo antes do código compilar. Fazendo seu programa mais robusto e garantindo que você tenha coberto todos os erros antes de sair para a produção. 3 | 4 | O Rust divide os erros em recuperáveis e não recuperáveis. Para erros recuperáveis, como um arquivo não achado, apenas queremos avisar ao usuário e tentar a operação novamente, já para erros irrecuperáveis, são sempre sintomas de bugs, como quando tentamos acessar um índice que passa do tamanho da array, então paramos imediatamente o programa. 5 | 6 | Usamos o tipo ```Result``` para erros recuperáveis e o macro ```panic!``` para parar a execução em erros irrecuperáveis. 7 | 8 | -------------------------------------------------------------------------------- /10 - Lidando Com Erro/02 - Erros Irrecuperáveis com Panic!.md: -------------------------------------------------------------------------------- 1 | 2 | Nos casos em que você não pode lidar com os erros, você usa o macro ```panic!```. Há duas maneiras de usar o panic na prática, pegando uma ação que causa um panico no nosso código, ou chamando o macro ```panic!``` explicitamente. 3 | 4 | ```rust 5 | fn main() { 6 | panic!("crash and burn"); 7 | } 8 | ``` 9 | 10 | Ao rodar o programa você vai ver: 11 | 12 | ```terminal 13 | $ cargo run 14 | Compiling panic v0.1.0 (file:///projects/panic) 15 | Finished dev [unoptimized + debuginfo] target(s) in 0.25s 16 | Running `target/debug/panic` 17 | thread 'main' panicked at 'crash and burn', src/main.rs:2:5 18 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 19 | ``` 20 | 21 | ## Usando Rastreamento de Retorno panic! 22 | 23 | ```rust 24 | fn main() { 25 | let v = vec![1, 2, 3]; 26 | 27 | v[99]; 28 | } 29 | ``` 30 | 31 | Quando tentamos acessar um valor inexistente no raio do nosso vetor, o Rust vai dar panico. 32 | 33 | -------------------------------------------------------------------------------- /01 - Introdução.md: -------------------------------------------------------------------------------- 1 | # INTRODUÇÃO 2 | 3 | Rust ajuda você a escrever rapidamente um software mais confiável. Ele te da a opção de controlar detalhes de baixo nível como uso de memória sem os problemas tradicionalmente associados a esse controle. 4 | 5 | O compilador do rust não permite você rodar códigos com bugs errôneos. Isso permite que os desenvolvedores gastem mais tempo na lógica do seu programa do que tentando corrigir bugs. 6 | 7 | Também traz junto algumas ferramentas bem úteis para desenvolvedores, como: 8 | - Cargo: Gerenciador de dependências e uma ferramenta de build, que adiciona, compila e controla as dependências entre o ecossistema do Rust. 9 | - Rustfmt: Ferramenta de formatação, para deixar o coding style consistente. 10 | - The Rust Language Server, que potencializa a integração da IDE para ajudar no código e as mensagens de erro embutidas. 11 | 12 | 13 | Uma importante parte no processo de aprender Rust é entender como ler as mensagens de erro do compilador, elas vão te guiar na direção de como trabalhar com o seu código. 14 | 15 | [Aqui está o passo a passo oficial para a instalação do Rust](https://doc.rust-lang.org/book/ch01-01-installation.html) 16 | -------------------------------------------------------------------------------- /08 - Packages, Crates e Módulos/02 - Packages e Crates.md: -------------------------------------------------------------------------------- 1 | # Packages e Crates 2 | 3 | Uma _crate_ é um pequeno amontoado de código que o compilador do Rust considera por vez, mesmo se você rodar um ```rustc``` no lugar do ```cargo``` e passar um arquivo de código sozinho, o compilador vai considerar esse arquivo uma crate. 4 | 5 | A _crate_ pode ter duas formas, uma binária ou uma biblioteca, as _crates binárias_ são programas que você pode compilar e rodar de forma independente. Cada uma deve ter a função ```main``` que define o que acontece quando o executável rodar. 6 | 7 | Uma _biblioteca crate_ não tem uma função ```main```, e elas não compilam para um executável. No lugar elas definem funcionalidades que podem ser compartilhados entre múltiplos projetos. 8 | 9 | A _crate root_ é um arquivo fonte que o compilador do Rust começa dele e cria o modulo raiz para sua _crate_. 10 | 11 | Um package é um pacote de uma ou mais _crates_ que dão um set de funcionalidades. Um _package_ contém um arquivo _Cargo.toml_ que descreve como buildar essas crates. Ele pode conter várias crates binárias mas apenas uma _crate_ de biblioteca. E um _package_ deve conter pelo menos uma _crate_, seja binária ou uma biblioteca. 12 | 13 | 14 | -------------------------------------------------------------------------------- /08 - Packages, Crates e Módulos/03 - Definindo Módulos para Controlar Escopo e Privacidade.md: -------------------------------------------------------------------------------- 1 | 2 | # Definindo Módulos para Controlar Escopo e Privacidade 3 | 4 | - Começa da raiz da crate: Quando compilamos uma crate, o compilador primeiramente olha a seu arquivo raiz (geralmente _src/lib.rs_) e procura um arquivo para compilar; 5 | - Declarando módulos: No arquivo raiz de uma crate você pode declarar seus novos módulos, ex, você declara um módulo "jardim" com ```mod: jardim;```. O compilador vai procurar pelo código do módulo nesses lugares: Em linha, também no arquivo _src/jardim.rs, e também no arquivo _src/jardim/mod.rs_ 6 | - Declarando submódulos: em qualquer outro arquivo que contém uma raiz de uma crate, você pode declarar submódulos, por exemplo, você quer declarar ```mod:vegetais``` em _src/jardim.rs_. O compilador vai procurar pelo modulo pai nesses lugares: Em linha, também no arquivo _src/jardim/vegetais_, e também em _src/jardim/vegetais/mod.rs_. 7 | - Caminhos para código em módulos: Uma vez que o módulo fizer parte da sua crate você se referir ao código em qualquer lugar da mesma crate, seguindo as regras de privacidade. Por exemplo, para usar um tipo ```Aspargus``` nos vegetais do jardim você pode encontrar ele em ```crate::jardim::vegetais::Aspargus```. 8 | - Privado vs Publico: Código dentro de um módulo é privado por padrão, você fazer ele público usando ```pub``` antes de declarar, ex: ```pub mod```. 9 | - Palavra-chave ```use```: Dentro de um escopo, ela cria um atalho para reduzir a repetição de longos caminhos, você pode se referir a um tipo dentro de um módulo com a palavra ```use```, assim consegue se referir a aquele item sem repetir o caminho inteiro dele. 10 | 11 | Código de exemplo: 12 | 13 | ```rust 14 | 15 | // src/garden/vegetais.rs 16 | use crate::garden::vegetables::Asparagus; 17 | 18 | pub mod garden; 19 | 20 | fn main() { 21 | let plant = Asparagus {}; 22 | println!("I'm growing {:?}!", plant); 23 | } 24 | ``` 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /11 - Genéricos, Traits e Lifetime/03 - Validando Referências com Lifetime.md: -------------------------------------------------------------------------------- 1 | # NAO FOI TERMINADO, PRECISA MELHORAR MUITO. 2 | 3 | Lifetimes (ciclos de vida) são outro tipo de genérico que já usamos. Diferente que garantir que um tipo tenha o comportamento que queremos, lifetimes garantem que as referências serão válidas contanto que precisamos que elas sejam. 4 | 5 | ## Prevenindo Referências Pendentes com Lifetimes 6 | 7 | Referências pendentes são referências que apontam para um dado inválido, e o Rust não gosta disso. 8 | 9 | Exemplo: 10 | 11 | ```rust 12 | fn main() { 13 | let r; 14 | 15 | { 16 | let x = 5; 17 | r = &x; 18 | } 19 | 20 | println!("r: {}", r); 21 | } 22 | ``` 23 | 24 | Declaramos a variável ```r``` e dentro de um novo escopo criamos a variável ```x```, tentamos colocar uma referência para ```x``` em ```r``` dentro do escopo, mas esse código irá quebrar, porque ao sair do escopo o ```x``` não existe mais e ```r``` estaria apontando para uma referência inválida (inexistente) agora. 25 | 26 | ## Lifetime Genérico em Funções 27 | 28 | Anotação genérica de lifetime descreve a relação entre o ciclo de vida de múltiplas referências e como elas estão relacionadas entre elas 29 | 30 | Vamos usar nesse código que cria duas String e depois chama uma função que recebe a referência dessas string e retorna a mais longa: 31 | 32 | ```rust 33 | fn main() { 34 | let string1 = String::from("abcd"); 35 | let string2 = String::from("xyz"); 36 | 37 | let result = longest(string1.as_str(), string2.as_str()); 38 | 39 | println!("A string mais longa é {}", result) 40 | } 41 | 42 | // &i32 -> referencia 43 | // &'a i32 -> referencia com ciclo de vida explicito 44 | // &'a mut i32 -> referencia mutavel com ciclo de vida explicito 45 | 46 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { 47 | if x.len() > y.len() { 48 | x 49 | } else { 50 | y 51 | } 52 | } 53 | ``` 54 | 55 | Anotações de lifetime usam ```'``` no começo seguido do nome, pode ser qualquer coisa. Assim estamos dizendo que ```x```, ```y``` e o valor de retorno da função tem seu ciclo de vida relacionado. -------------------------------------------------------------------------------- /09 - Coleções Comuns/01 - Guardando Lista de Valores com Vetores.md: -------------------------------------------------------------------------------- 1 | # Guardando Lista de Valores com Vetores 2 | 3 | O primeiro tipo de coleção que vamos ver é ```Vec```, também conhecido como um vetor, ele permite que você armazene mais de um valor em uma estrutura de dados única, colocando os valores pŕoximos um do outro na memória. Eles só armazenam valores do mesmo tipo, e são muito úteis quando você tem uma lista de itens, como as linhas de texto em um arquivo ou preços dos itens em um carrinho de mercado. 4 | 5 | ## Criando um Novo Vetor 6 | 7 | ```rust 8 | let v: Vec = Vec::new(); 9 | ``` 10 | 11 | Declaramos um vetor passando o tipo dele, pois o compilador precisa saber qual o tipo de elementos que ele vai guardar. 12 | 13 | Rust também tem o macro ```vec!``` que cria um novo vetor que segura os valores que você passar. 14 | 15 | ```rust 16 | let v = vec![1, 2, 3]; 17 | ``` 18 | 19 | ### Atualizando um Vetor 20 | 21 | Usamos um método push para adicionar elementos a um vetor 22 | 23 | ```rust 24 | let mut v = Vec::new(); 25 | 26 | v.push(5); 27 | v.push(6); 28 | v.push(7); 29 | v.push(8); 30 | ``` 31 | 32 | ### Lendo Elementos de Vetores 33 | 34 | Tem duas maneiras de referenciar a um valor armazenado em um vetor, pelo index ou usando o método ```get```. 35 | 36 | ```rust 37 | let v = vec![1, 2, 3, 4, 5]; 38 | 39 | let third: &i32 = &v[2]; 40 | println!("O terceiro elemento é {third}"); 41 | 42 | let third: Option<&i32> = v.get(2); 43 | match third { 44 | Some(third) => println!("O terceiro elemento é {third}"), 45 | None => println!("Não tem terceiro elemento."), 46 | } 47 | ``` 48 | 49 | Com o método ```get``` recebemos um ```Option<&T>``` que pode ser usado com ```match```. 50 | 51 | Quando queremos um index que passou no tamanho do vetor, usar ```[]``` vai fazer um pânico e quebrar o seu programa, já o método ```get``` vai retornar ```None```, permitindo que você lide com o programa. 52 | 53 | ### Iterando sobre Valores em um Vetor 54 | 55 | Para acessar cada elemento de um vetor podemos usar o ```for``` loop. 56 | 57 | ```rust 58 | let v = vec![100, 32, 57]; 59 | for i in &v { 60 | println!("{i}"); 61 | } 62 | ``` 63 | 64 | Também podemos iterar sobre referências mutáveis para cada elemento no vetor. 65 | 66 | ```rust 67 | let mut v = vec![100, 32, 57]; 68 | for i in &mut v { 69 | *i += 50; 70 | } 71 | ``` 72 | 73 | 74 | ### Soltar um Vetor Também Solta Os Seus Elementos 75 | 76 | Como qualquer outra estrutura, um vetor deixa de existir quando sai do escopo, o mesmo acontece com os elementos dentro dele. 77 | 78 | ```rust 79 | { 80 | let v = vec![1, 2, 3, 4]; 81 | 82 | // do stuff with v 83 | } // <- v goes out of scope and is freed here 84 | ``` 85 | 86 | 87 | -------------------------------------------------------------------------------- /10 - Lidando Com Erro/03 - Erros Recuperáveis com Result.md: -------------------------------------------------------------------------------- 1 | 2 | # Erros Recuperáveis com Result 3 | 4 | A maioria dos erros não são sérios o suficiente para fazer o programa paras, as vezes, quando uma função falha, é por uma razão que você pode interpretar e responder a ela. Por exemplo, se você tentar abrir um arquivo e ele não existir, ao invés de parar o programa, podemos criar o arquivo. 5 | 6 | Lembre do enum ```Result```: 7 | 8 | ```rust 9 | enum Result { 10 | Ok(T), 11 | Err(E), 12 | } 13 | ``` 14 | 15 | Onde os tipos ```T```, e ```E``` são tipos genéricos, no qual, ```T``` corresponde ao tipo do valor que será retornado em caso de sucesso, e ```E``` o tipo de erro em uma falha. 16 | 17 | Vamos chamar uma função que retorna um valor ```Result```, tentaremos abrir um arquivo: 18 | 19 | ```rust 20 | use std::fs::File; 21 | 22 | fn main() { 23 | let greeting_file_result = File::open("hello.txt"); 24 | } 25 | ``` 26 | 27 | Nesse caso, se ```File::open``` der sucesso, o valor retornado será uma instância de Ok, que contém um manipulador de arquivos, se falhar, será uma instância de Err, que contém mais informações sobre o tipo de erro que aconteceu. 28 | 29 | Podemos lidar com os dois tipos de resultado usando ```match```: 30 | 31 | ```rust 32 | use std::fs::File; 33 | 34 | fn main() { 35 | let greeting_file_result = File::open("hello.txt"); 36 | 37 | let greeting_file = match greeting_file_result { 38 | Ok(file) => file, 39 | Err(error) => panic!("Problem opening the file: {:?}", error), 40 | }; 41 | } 42 | ``` 43 | 44 | Também podemos usar o enum ```io::ErrorKind``` para verificar o tipo de erro e tomar ações baseadas nele, por exemplo, se o motivo do erro for um arquivo não encontrado, então criamos um novo: 45 | 46 | ```rust 47 | use std::fs::File; 48 | use std::io::ErrorKind; 49 | 50 | fn main() { 51 | let greeting_file_result = File::open("hello.txt"); 52 | 53 | let greeting_file = match greeting_file_result { 54 | Ok(file) => file, 55 | Err(error) => match error.kind() { 56 | ErrorKind::NotFound => match File::create("hello.txt") { 57 | Ok(fc) => fc, 58 | Err(e) => panic!("Problem creating the file: {:?}", e), 59 | }, 60 | other_error => { 61 | panic!("Problem opening the file: {:?}", other_error); 62 | } 63 | }, 64 | }; 65 | } 66 | ``` 67 | 68 | 69 | ### Atalhos para o Erro Panic! 70 | 71 | Podemos usar o método ```unwrap``` como um atalho que retornará o arquivo, em caso de sucesso, e irá chamar ```panic!``` em caso de erro. 72 | 73 | ```rust 74 | use std::fs::File; 75 | 76 | fn main() { 77 | let greeting_file = File::open("hello.txt").unwrap(); 78 | } 79 | ``` 80 | 81 | Também temos o método ```expect``` que nos permite passar uma mensagem personalizada ao ```panic!```: 82 | 83 | ```rust 84 | use std::fs::File; 85 | 86 | fn main() { 87 | let greeting_file = File::open("hello.txt") 88 | .expect("hello.txt should be included in this project"); 89 | } 90 | ``` 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /04 - Conceitos comuns/01 - Variáveis e Mutabilidade.md: -------------------------------------------------------------------------------- 1 | # Variáveis e Mutabilidade 2 | 3 | Por padrão, variáveis no Rust são imutáveis. Isso é uma das cutucadas que o Rust da para escrever códigos seguros. 4 | 5 | Se você tentar rodar o código abaixo, terá um erro no terminal: 6 | 7 | ```rust 8 | fn main() { 9 | let x = 5; 10 | println!("O valor de x é: {x}"); 11 | x = 6; 12 | println!("O novo valor de x é: {x}"); // nao roda 13 | } 14 | ``` 15 | 16 | O erro será porque você tentou modificar o valor de uma variável imutável. Esses erros na hora de compilar são importantes e nos ajudam a prevenir de bugs depois que nossa aplicação já estiver compilada. Se uma parte do seu código assumir que a variável não vai mudar mas em outra parte o nosso código mudar esse valor, é possível que não rode como o esperado, vai ser difícil de rastrear esse bug. 17 | 18 | Mas mutabilidade pode ser bem útil, para criar uma variável mutável você precisa adicionar ```mut``` antes do nome da variável, isso também indica para quem estiver lendo o código que aquela variável vai ser alterada futuramente: 19 | 20 | ```rust 21 | fn main() { 22 | let mut x = 5; 23 | println!("O valor de x é: {x}"); 24 | x = 6; 25 | println!("O novo valor de x é: {x}"); // funciona 26 | } 27 | ``` 28 | 29 | ### Constantes 30 | 31 | Assim como variáveis imutáveis, constantes são valores que são designados para serem de um tipo e não podem ser alterados. Mas há algumas diferenças entre constantes e variáveis: 32 | 33 | - Primeiramente você não pode usar ```mut``` com constantes, elas sempre serão imutáveis, e para criar uma constante você usa ```const``` ao invés de ```let```, seu tipo de valor sempre será anotado, mas vamos ver mais pra frente. 34 | 35 | - Constantes podem ser declaradas em qualquer escopo, inclusive no global. O que faz elas serem úteis para serem usadas em várias partes do código. 36 | 37 | - A última diferença é que constantes só podem ser associadas a uma expressão constante, e não o resultado de algo que só vai ser computado na hora de rodar o código. 38 | 39 | ```rust 40 | const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3; 41 | ``` 42 | 43 | Operações em constantes são feitas na hora de compilar o código e tem os valores salvo nos binários, deixando assim o código mais rápido. 44 | 45 | ### Sombreamento 46 | 47 | Como vimos antes, podemos declarar mais de uma variável com o mesmo nome, popularmente é falado que a primeira variável é _sombreada_ pela segunda, isso quer dizer que a segunda variável é o que o compilador vai ver quando você usar o nome da variável. Em efeito, a segunda variável ofusca a primeira. Nós podemos sombrear uma variável usando o mesmo nome dela e repetindo o uso da palavra-chave. 48 | 49 | ```rust 50 | fn main() { 51 | let x = 5; 52 | 53 | let x = x + 1; // 6 54 | 55 | { 56 | let x = x * 2; 57 | println!("O valor de x dentro do escopo é: {x}"); // 12 58 | } 59 | 60 | println!("O valor de x fora do escopo é: {x}"); // 6 61 | } 62 | ``` 63 | 64 | Sombrear (shadowing) é como alterar uma variável mas sem alterar, por exemplo no código acima, aonde depois de sair do outro escopo eu queria continuar com o valor que x teve no começo. 65 | 66 | Também colocar tipos diferentes nas variáveis: 67 | 68 | ```rust 69 | let x = 10; 70 | let x = "X is a string"; 71 | let x = true; 72 | ``` 73 | 74 | -------------------------------------------------------------------------------- /02 - Começando/01 - Hello, World!.md: -------------------------------------------------------------------------------- 1 | # Hello, World! 2 | 3 | Criando uma pasta para o nosso primeiro programa em rust: 4 | 5 | ``` console 6 | $ mkdir ~/projects 7 | $ cd ~/projects 8 | $ mkdir hello_world 9 | $ cd hello_world 10 | ``` 11 | 12 | Agora vamos criar um novo arquivo e chamar ele de **main.rs**, arquivos rust sempre terminam com ".rs". Se você for criar um arquivo com mais de uma palavra, por convenção, prefira usar underline para separar elas, como _hello_world.rs_ ao invés de _helloworld.rs_. 13 | 14 | Agora no arquivo, vamos digitar nosso primeiro código: 15 | 16 | ```rust 17 | fn main() { 18 | println!("Hello, world!"); 19 | } 20 | ``` 21 | 22 | Agora podemos voltar ao terminal e para compilar e rodar o arquivo podemos digitar os seguintes comandos: 23 | 24 | #### Linux ou Mac Os: 25 | ```console 26 | $ rustc main.rs #compila o arquivo 27 | $ ./main #roda o arquivo executável criado pelo compilador 28 | Hello, world! #output 29 | ``` 30 | 31 | #### Windows 32 | ```console 33 | > rustc main.rs 34 | > .\main.exe 35 | 36 | Hello, world! 37 | ``` 38 | 39 | Agora você é um programador Rust! 40 | 41 | ### Anatomia de um Programa em Rust 42 | 43 | A primeira parte: 44 | 45 | ```rust 46 | fn main() { 47 | // ... 48 | } 49 | ``` 50 | 51 | Aqui criamos uma função nomeada ```main```, ela é uma função especial, será sempre a primeira parte do código que vai rodar em todo programa executável em Rust. Nela não vai nenhum parâmetro e também não retorna nada. Se precisássemos de parâmetros eles iriam dentro dos parênteses ( ). 52 | 53 | O corpo da função esta envolto dos ```{}``` (Curly brackets). O rust precisa de curly brackets em volta de todos os corpos das funções. 54 | 55 | O corpo da função `main` agora segura o seguinte código: 56 | 57 | ```rust 58 | println!("Hello, world!"); 59 | ``` 60 | 61 | Essa é a linha que faz todo o trabalho nesse pequeno programa, ela imprime o texto na tela. 62 | Temos alguns detalhes importantes para ver aqui: 63 | 64 | - O estilo de código do Rust é identado por 4 espaços, não uma tab. 65 | - ```println!``` chama um macro do Rust, se fosse chamado como uma função seria ```println```sem usar o ```!```. Vamos ver macros detalhadamente mais pra frente, por enquanto apenas precisamos saber que usar um ```!``` significa que estou chamando um macro ao invés de uma função normal e que os macros não seguem sempre as mesmas regras das funções. 66 | - A string ```Hello, world!``` é passada como um argumento para o macro ```println!``` e ela imprime a string na tela. 67 | - Precisamos terminar a linha com ```;``` para indicar que a expressão acabou e estamos prontos para seguir com o código. 68 | 69 | ### Compilar e Rodar o Programa São Passos Separados. 70 | 71 | Acabamos de criar um programa, vamos examinar cada passo desse processo. 72 | 73 | Antes de rodar o programa Rust você deve compilar ele usando o compilador do Rust, usando o comando ```rustc``` e passando o nome do arquivo do nosso código, assim: 74 | 75 | ```console 76 | $ rustc main.rs 77 | ``` 78 | 79 | Se o código for compilado com sucesso, sem erros, será gerado um executável binário. Que você pode rodar digitando no terminal: 80 | 81 | ```console 82 | $ ./main # ou .\main.exe no Windows. 83 | ``` 84 | 85 | Rust é uma linguagem compilada a frente do tempo! Quer dizer que você consegue compilar o programa e dar o executável para qualquer pessoa e elas vão conseguir rodar o código mesmo sem ter o Rust instalado. Se você dar a alguém um .rb, .py ou um arquivo .js, elas vão precisar ter uma implementação de Ruby, Python ou Javascript instalada. Mas nessas linguagens você só precisa de um comando para compilar e rodar o programa, tudo é uma troca do design da linguagem. -------------------------------------------------------------------------------- /04 - Conceitos comuns/03 - Funções.md: -------------------------------------------------------------------------------- 1 | # Funções 2 | 3 | Já vimos a função mais importante do Rust: a ```main``` que é a porta de entrada para todos os programas em Rust. Também já vimos a palavra ```fn``` que nos permite criar novas funções. 4 | 5 | Rust usa snake_case em nomes de funções e de variáveis: 6 | 7 | ```rust 8 | fn main() { 9 | println!("Hello, world!"); 10 | 11 | another_function(); 12 | } 13 | 14 | fn another_function() { 15 | println!("Outra função."); 16 | } 17 | ``` 18 | 19 | Definimos uma nova função em Rust usando ```fn``` e () depois do nome dela, as {} indicam o corpo da função. 20 | 21 | O Rust não se importa com a ordem de declaração da função, então você pode chamar ela antes de declarar, só tem que estar no escopo acessível. 22 | 23 | ### Parâmetros 24 | 25 | Nós podemos definir funções com parâmetros, que são variáveis especiais que fazem par te da função, quando ela receber parâmetros podemos enviar os valores deles na chamada da função, chamamos os valores passados de argumentos: 26 | 27 | ```rust 28 | fn main() { 29 | another_function(5); 30 | } 31 | 32 | fn another_function(x: i32) { 33 | println!("O valor de x é: {x}"); 34 | } 35 | ``` 36 | 37 | Você deve definir o tipo de cada parâmetro na hora de criar a função, ai definimos que x é do tipo ```i32```. 38 | 39 | 40 | ### Declarações (Statements) e Expressões 41 | 42 | - **Declarações**: instruções que performam alguma ação e não retornam nenhum valor. 43 | - **Expressões**: Retornam algum valor. 44 | 45 | Criar uma variável e associar ela a um valor é uma declaração. 46 | 47 | ```rust 48 | fn main() { 49 | let y = 6; 50 | } 51 | ``` 52 | 53 | Declarações não retornam nenhum valor, então você não pode associar uma declaração ```let``` para outra variável, você vai ter um erro se fizer isso: 54 | 55 | ```rust 56 | fn main() { 57 | let x = (let y = 6); 58 | } 59 | ``` 60 | 61 | o ```let y = 6``` não retorna nenhum valor, então não tem nada para associar a variável x. 62 | 63 | Expressões podem ser parte de declarações, o ```6``` em ```let y = 6``` é uma expressão que resulta no valor 6, chamar uma função é uma expressão, chamar um macro é uma expressão, e um novo escopo em um bloco com ```{}``` também é uma expressão: 64 | 65 | ```rust 66 | fn main() { 67 | let y = { 68 | let x = 3; 69 | x + 1 70 | }; 71 | 72 | println!("O valor de y é: {y}"); 73 | } 74 | ``` 75 | 76 | A expressão: 77 | 78 | ```rust 79 | { 80 | let x = 3; 81 | x + 1 82 | } 83 | ``` 84 | 85 | - É um boco que vai resultar no valor 4. 86 | - Expressões não usam ```;``` no final para retornar, se você usar ```;``` no valor a ser retornado você vai criar uma declaração e não uma expressão que retorna algo. 87 | 88 | 89 | ### Funções Com Valor de Retorno 90 | 91 | Funções podem retornar valores e podemos definir o tipo do valor a ser retornado com ```->```. 92 | 93 | Você pode retornar valores em uma função com a palavra ```return``` mas a maioria das funções retornam a última expressão implícita, que não pode ter ```;```. 94 | 95 | ```rust 96 | fn five() -> i32 { 97 | 5 98 | } 99 | 100 | fn main() { 101 | let x = five(); 102 | 103 | println!("O valor de x é: {x}"); // X é 5. 104 | } 105 | ``` 106 | 107 | A função ```five``` acima retorna o valor ```5```, depois associamos esse valor retornado a variável ```x```. 108 | 109 | Se tentarmos fazer uma função e no final na última linha que deveria ser retornada colocarmos ```;``` assim: 110 | 111 | ```rust 112 | fn main() { 113 | let x = plus_one(5); 114 | 115 | println!("O valor de x é: {x}"); 116 | } 117 | 118 | fn plus_one(x: i32) -> i32 { 119 | x + 1; 120 | } 121 | ``` 122 | 123 | Vai gerar um erro, porque transformamos uma expressão em uma declaração. 124 | 125 | -------------------------------------------------------------------------------- /09 - Coleções Comuns/03 - Armazenando Keys com Valores Associados em Hash Maps.md: -------------------------------------------------------------------------------- 1 | # Armazenando Keys com Valores Associados em Hash Maps 2 | 3 | A última das nossas coleções comuns é o _hash map_. O tipo ```HashMap``` armazena um mapeando das keys do tipo ```K``` para os valores do tipo ```V``` usando funções hash, que determina como isso coloca os valores na memória. 4 | 5 | Eles são úteis quando você quer procurar dados sem usar indices. Por exemplo, em um jogo, você pode rastrear cada pontuação de cada time em um hash map, onde cada key é o nome do tipo e os valores dentro da key são as pontuações. 6 | 7 | ### Criando um Novo Hash Map 8 | 9 | Uma maneira de criar um hash map vazio é usando ```new``` e depois adicionar os seus elementos com ```insert```. Aqui criamos dois times e suas pontuações, o primeiro parâmetro é a chave e o segundo é o valor associado a chave. 10 | 11 | ```rust 12 | use std::collections::HashMap; 13 | 14 | let mut scores = HashMap::new(); 15 | 16 | scores.insert(String::from("Blue"), 10); 17 | scores.insert(String::from("Yellow"), 50); 18 | ``` 19 | 20 | Assim como vetores, hash maps armazenam seus dados na heap, e são homogêneos, todas as suas chaves e valores devem possuir o mesmo tipo. 21 | 22 | ### Acessando Valores em Hash Maps 23 | 24 | Para pegarmos um valor de um hash map usando uma key, usamos o método ```get```: 25 | 26 | ```rust 27 | use std::collections::HashMap; 28 | 29 | let mut scores = HashMap::new(); 30 | 31 | scores.insert(String::from("Blue"), 10); 32 | scores.insert(String::from("Yellow"), 50); 33 | 34 | let team_name = String::from("Blue"); 35 | let score = scores.get(&team_name).copied().unwrap_or(0); 36 | ``` 37 | 38 | O método ```get``` retorna um tipo ```Option<&V>```, se a key não tiver valor, será retornado none, então usamos ```copied``` para criar uma cópia daquele valor e não mais uma referência, depois ```unwrap_or``` para setar a pontuação para zero caso não exista nada associado a aquela chave. 39 | 40 | Também podemos iterar sobre cada chave e valor com um ```for``` loop: 41 | 42 | ```rust 43 | use std::collections::HashMap; 44 | 45 | let mut scores = HashMap::new(); 46 | 47 | scores.insert(String::from("Blue"), 10); 48 | scores.insert(String::from("Yellow"), 50); 49 | 50 | for (key, value) in &scores { 51 | println!("{key}: {value}"); 52 | } 53 | ``` 54 | 55 | ### Atualizando um Hash Map 56 | 57 | As chaves do hash map não podem se repetir, não podem ter o mesmo nome. 58 | 59 | Quando você quer mudar um dado em um hash map você tem que decidir o que fazer em caso que a chave já tenha um valor associado, se você quer ignorar, sobrescrever ou combinar.. 60 | 61 | ### Sobrescrevendo um Valor 62 | 63 | Se inserirmos um novo valor com a mesma chave, o valor associado a chave será sobrescrito, assim: 64 | 65 | ```rust 66 | use std::collections::HashMap; 67 | 68 | let mut scores = HashMap::new(); 69 | 70 | scores.insert(String::from("Blue"), 10); 71 | scores.insert(String::from("Blue"), 25); 72 | 73 | println!("{:?}", scores); // 25 74 | ``` 75 | 76 | Agora, a chave ```Blue``` tem valor ```25```. 77 | 78 | ### Adicionar Apenas Se Não Estiver Presente 79 | 80 | Para checar se uma chave já existe e apenas inserir se ela não existir podemos usar o método```entry``` e ```or_insert```: 81 | 82 | ```rust 83 | use std::collections::HashMap; 84 | 85 | let mut scores = HashMap::new(); 86 | scores.insert(String::from("Blue"), 10); 87 | 88 | scores.entry(String::from("Yellow")).or_insert(50); 89 | scores.entry(String::from("Blue")).or_insert(50); 90 | 91 | println!("{:?}", scores); 92 | ``` 93 | 94 | ### Atualizando Chaves Baseado no Valor Antigo 95 | 96 | Outro caso comum para hash maps é atualizar o valor baseado no valor antigo. 97 | 98 | Esse código conta quantas vezes a palavra se repete, se for a primeira vez que vemos uma palavra, o primeiro valor inserido será 0. 99 | 100 | ```rust 101 | use std::collections::HashMap; 102 | 103 | let text = "hello world wonderful world"; 104 | 105 | let mut map = HashMap::new(); 106 | 107 | for word in text.split_whitespace() { 108 | let count = map.entry(word).or_insert(0); 109 | *count += 1; 110 | } 111 | 112 | println!("{:?}", map); 113 | ``` 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /11 - Genéricos, Traits e Lifetime/02 - Traits, Definindo Comportamento Compartilhado.md: -------------------------------------------------------------------------------- 1 | 2 | # Traits, Definindo Comportamento Compartilhado 3 | 4 | Uma trait nos permite a definir um set de métodos que são compartilhados por diferentes tipos. 5 | ## Definindo uma Trait 6 | 7 | Vamos supor que temos duas estruturas ```NewsArticle``` e ```Tweet``` e queremos que essas duas tenham um método que nos possibilite imprimir um resumo na tela. 8 | 9 | ```rust 10 | pub struct NewsArticle { 11 | pub headline: String, 12 | pub location: String, 13 | pub author: String, 14 | pub content: String, 15 | } 16 | 17 | impl Summary for NewsArticle { 18 | fn summarize(&self) -> String { 19 | format!("{}, by {}", self.headline, self.author) 20 | } 21 | } 22 | 23 | pub struct Tweet { 24 | pub username: String, 25 | pub content: String, 26 | pub reply: bool, 27 | pub retweet: bool, 28 | } 29 | 30 | impl Summary for Tweet { 31 | fn summarize(&self) -> String { 32 | format!("{}: {}", self.username, self.content) 33 | } 34 | } 35 | 36 | pub trait Summary { 37 | fn summarize(&self) -> String; 38 | } 39 | 40 | fn main() { 41 | let tweet = Tweet { 42 | username: String::from("joao"), 43 | content: String::from("poca pica"), 44 | reply: true, 45 | retweet: true, 46 | }; 47 | 48 | let article = NewsArticle { 49 | author: String::from("jao"), 50 | content: String::from("muita bala"), 51 | headline: String::from("blablabla"), 52 | location: String::from("socorro"), 53 | }; 54 | 55 | println!("Tweet summary: {}", tweet.summarize()); 56 | println!("Article summary: {}", article.summarize()); 57 | } 58 | 59 | ``` 60 | 61 | Criamos uma trait Summary com a palavra-chave ```trait```, note que apenas definimos a assinatura da função, não é a função em si. Isso diz que pra cada tipo que implementa essa trait, ele deve ter o método summarize(). 62 | 63 | Depois implementamos esse método definido na trait nos nossos dois tipos criados, cada um com a mesma assinatura porém com sua forma de lidar com o tipo. 64 | 65 | Também podemos colocar uma implementação padrão na declaração da trait, que pode ser sobrescrita: 66 | 67 | ```rust 68 | pub trait Summary { 69 | fn summarize(&self) -> String { 70 | String::from("Read more..") 71 | } 72 | } 73 | ``` 74 | 75 | Traits permitem adicionar mais que um método para os tipos: 76 | 77 | ```rust 78 | impl Summary for Tweet { 79 | fn summarize(&self) -> String { 80 | format!("{}: {}", self.username, self.content) 81 | } 82 | 83 | fn summarize_author(&self) -> String { 84 | self.username.clone() 85 | } 86 | } 87 | 88 | pub trait Summary { 89 | fn summarize(&self) -> String { 90 | String::from("Read more..") 91 | } 92 | 93 | fn summarize_author(&self) -> String; 94 | } 95 | ``` 96 | 97 | ### Trait Bounds 98 | 99 | Agora vamos falar sobre traits como parâmetros: 100 | 101 | ```rust 102 | pub fn notify(item: &impl Summary) { 103 | println!("Breaking news! {}", item.summarize()); 104 | } 105 | ``` 106 | 107 | Criamos uma função que recebe como parâmetro uma referência para qualquer tipo que implemente a trait Summary. Assim podemos passar a nossa estrutura ```article``` ou ```tweet``` que implementam a trait Summary: 108 | 109 | ```rust 110 | notify(&article); 111 | ``` 112 | 113 | Podemos usar genéricos também, para dizer que um ou mais parâmetros são do mesmo tipo: 114 | 115 | ```rust 116 | pub fn notify2(item1: &T, item2: &T) { 117 | //.... 118 | } 119 | ``` 120 | 121 | Também da de definir múltiplas traits com a sintaxe ```impl```: 122 | 123 | ```rust 124 | pub fn notify3(item1: &(impl Summary + Display), item2: &impl Summary) { 125 | //.... 126 | } 127 | ``` 128 | 129 | ### Retornando Tipos que Implementam Traits 130 | 131 | Podemos usar a sintaxe ```impl Trait``` no retorno de uma função para indicar que vamos retornar o valor de algum tipo que implementa essa trait: 132 | 133 | ```rust 134 | fn returns_summarizable() -> impl Summary { 135 | Tweet { 136 | username: String::from("horse_ebooks"), 137 | content: String::from( 138 | "of course, as you probably already know, people", 139 | ), 140 | reply: false, 141 | retweet: false, 142 | } 143 | } 144 | ``` 145 | 146 | -------------------------------------------------------------------------------- /02 - Começando/02 - Hello, Cargo!.md: -------------------------------------------------------------------------------- 1 | # Hello, Cargo! 2 | 3 | O Cargo é um sistema de build e gerenciador de pacotes do Rust, é muito usado porque o Cargo da conta de um monte de tarefas para você, como fazer a build do código, instalar bibliotecas e dependências do código, além de construir essas bibliotecas. 4 | 5 | Se você instalou rust seguindo a documentação do Rust você poderá digitar o seguinte comando pra ver a versão do Cargo que você está usando, se não for encontrado, procure como instalar o Cargo separadamente. 6 | 7 | ```console 8 | $ cargo --version 9 | ``` 10 | 11 | 12 | ### Criando um projeto com Cargo 13 | 14 | Vamos iniciar um novo projeto usando o Cargo, você vai ver a diferença dele com o nosso programa original "Hello, World!". 15 | 16 | Primeiro vamos criar um novo projeto e acessar a sua pasta. 17 | 18 | ```console 19 | $ cargo new hello_cargo 20 | $ cd hello_cargo 21 | ``` 22 | 23 | O primeiro comando cria um novo diretório e um projeto chamado hello_cargo, agora podemos ir na nossa pasta que foi criada e ver a lista dos arquivos, o Cargo criou dois arquivos para nós: _Cargo.toml_ e um diretório _src_ com um arquivo _main.rs_ dentro dele. 24 | 25 | Se você não estiver dentro de um repositório git o cargo irá gerar um arquivo .gitignore, caso o contrario você vai ter que usar a flag ```--vcs=git``` para criar. 26 | 27 | Abrindo o arquivo _Cargo.toml_ você vai ver isso: 28 | 29 | ```toml 30 | [package] 31 | name = "hello_cargo" 32 | version = "0.1.0" 33 | edition = "2021" 34 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 35 | 36 | [dependencies] 37 | ``` 38 | 39 | Esse é um arquivo de configuração de uma aplicação Rust usando o Cargo. 40 | 41 | A primeira linha ```[package]``` é a seção de heading que indica as declarações que estão configurando o pacote, as próximas três linhas são as informação que o Cargo precisa para compilar o nosso programa, o nome, a versão e a edição do Rust que vai usar. 42 | 43 | A ultima linha ```[dependencies]``` é o começo da seção para você listar qualquer dependência do seu projeto, em Rust, os pacotes de código são chamados de _crates_. 44 | 45 | Agora podemos abrir _src/main.rs_ e dar uma olhada: 46 | 47 | ```rust 48 | fn main() { 49 | println!("Hello, world!"); 50 | } 51 | ``` 52 | 53 | O Cargo gerou um programa "Hello, world!", igual fizemos anteriormente, até aqui as diferenças do nosso primeiro projeto e o projeto Cargo é que o Cargo colocou o código dentro do diretório _src_ e agora nós temos um arquivo de configuração _Cargo.toml_. 54 | 55 | Cargo espera que todo seu código fonte esteja dentro do diretório _src_, o diretório onde temos o _Cargo.toml_ é apenas para arquivos README, informações de licença, e nada relacionado ao código. 56 | 57 | ### Compilar e Rodar um Projeto Cargo 58 | 59 | Para fazer a build do nosso projeto podemos digitar o comando: 60 | 61 | ```console 62 | $ cargo build 63 | ``` 64 | 65 | Esse comando irá gerar um arquivo executável em _/target/debug/hello_cargo_. O arquivo padrão de build é uma build de debug, o Cargo coloca os binários em um diretório chamado debug, agora podemos rodar o executável rodando o seguinte comando: 66 | 67 | ```console 68 | $ ./target/debug/hello_cargo # ou hello_cargo.exe no Windows 69 | Hello, world! 70 | ``` 71 | 72 | Se tudo ocorrer corretamente você vai ter um ```Hello, world!``` impresso no seu terminal, rodar o comando ```cargo build``` pela primeira vez também vai gerar um arquivo Cargo.lock, esse arquivo acompanha as exatas versões das dependências do seu projeto. Você nunca vai precisar mudar isso manualmente, o Cargo cuidará disso. 73 | 74 | Nós também podemos usar ```cargo run``` para compilar o código e rodar o executável final em apenas um comando: 75 | 76 | ```console 77 | $ cargo run 78 | Hello, world! 79 | ``` 80 | 81 | Usar ```cargo run``` é mais conveniente que ter que rodar ```cargo build``` e depois usar todo o caminho para o binário, então a maioria dos desenvolvedores usam ```cargo run```. Sempre que o nosso código mudar você vai poder ver uma mensagem na tela que diz que o ```cargo run``` está compilando o código fonte. 82 | 83 | Para apenas checar se seu código compila mas não gerar nenhum executável você pode usar ```cargo check```. Por que você usaria isso? Ele é muito mais rápido que ```cargo build```porque ele pula a parte de produzir um executável. Você pode usar o _check_ para verificar se está tudo certo durante o desenvolvimento e quando acabar você pode fazer a _build_. 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /12 - Testes/01 - Como Escrever Testes.md: -------------------------------------------------------------------------------- 1 | # Como Escrever Testes 2 | 3 | ## Por que Escrever Testes? 4 | 5 | O Rust já faz um ótimo trabalho cuidando dos nossos tipos, e outras coisas como o verificador de empréstimos (borrow checker), verificando se não estamos gerenciado mal a nossa memória, mas, não conseguimos realmente checar se as funções estão fazendo a coisa certa, a lógica de negócio, isso cuidamos nos nossos testes. 6 | 7 | ## Criando Testes 8 | 9 | Primeiro, vamos usar o ```cargo new adder --lib``` para criar uma nova biblioteca para nossos testes, após criar, dentro do arquivo _src/lib.rs_, você vai encontrar um módulo de testes já escrito: 10 | 11 | ```rust 12 | pub fn add(left: usize, right: usize) -> usize { 13 | left + right 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | 20 | #[test] 21 | fn it_works() { 22 | let result = add(2, 2); 23 | assert_eq!(result, 4); 24 | } 25 | } 26 | ``` 27 | 28 | Em Rust, as funções de teste tem o atributo ```#[test]```. 29 | 30 | Para rodar os testes, usamos ```cargo test```. 31 | 32 | Vamos ter algo assim no terminal, indicando quais testes passaram e quais falharam: 33 | 34 | ```bash 35 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 36 | ``` 37 | 38 | ### Teste Que Falha 39 | 40 | Dentro do nosso módulo de testes, vamos escrever uma nova função, que lançará um ```panic!```, para falhar o teste: 41 | 42 | ```rust 43 | #[test] 44 | fn failing_test() { 45 | panic!("Faça esse teste falhar") 46 | } 47 | ``` 48 | 49 | Agora, rodando o teste, vamos ver que temos uma falha: 50 | 51 | ```bash 52 | test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 53 | ``` 54 | 55 | 56 | ## Testando Código de Produto 57 | 58 | Vamos criar a estrutura de um retângulo e um método que verifica se um retângulo é maior que o outro, depois nos testes vamos retornar se foi verdadeiro com falso com o macro de acerto ```assert!``` que retorna true/false. 59 | 60 | ```rust 61 | #[derive(Debug)] 62 | struct Rectangle { 63 | width: u32, 64 | height: u32, 65 | } 66 | 67 | impl Rectangle { 68 | fn can_hold(&self, other: &Rectangle) -> bool { 69 | self.width > other.width && self.height > other.height 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use super::*; 76 | 77 | #[test] 78 | fn larger_can_hold_smaller() { 79 | let larger = Rectangle { 80 | width: 8, 81 | height: 7, 82 | }; 83 | 84 | let smaller = Rectangle { 85 | width: 5, 86 | height: 1, 87 | }; 88 | 89 | assert!(larger.can_hold(&smaller)); 90 | } 91 | } 92 | ``` 93 | 94 | Esse teste vai passar com sucesso! Pois ```larger``` consegue conter ```smaller```. 95 | 96 | ### Macro assert! 97 | 98 | O macro ```assert!``` também nos permite comparar dois valores: 99 | 100 | ```rust 101 | pub fn add_two(a: i32) -> i32 { 102 | a + 2 103 | } 104 | 105 | #[cfg(test)] 106 | mod tests { 107 | use super::*; 108 | 109 | #[test] 110 | fn it_adds_two() { 111 | assert!(4, add_two(2)); 112 | } 113 | } 114 | ``` 115 | 116 | O macro ```assert!``` compara se o resultado do segundo parâmetro é igual o primeiro. 117 | 118 | Também tem o macro ```assert_eq!``` que retornará ```true``` caso os dois parâmetros passados não sejam iguais, ex: ```assert_eq!(4, 4)``` será verdadeiro. 119 | 120 | 121 | ## Testes Para Garantir Falhas 122 | 123 | Quando queremos que uma função falhe para passar no teste, usamos o atributo ```#[should_panic]```, a função deveria falhar, mas, vai passar nos testes, pois é isso que estávamos esperando: 124 | 125 | ```rust 126 | #[cfg(test)] 127 | mod tests { 128 | use super::*; 129 | 130 | #[test] 131 | #[should_panic] 132 | fn it_adds_two() { 133 | assert_eq!(4, 2); 134 | } 135 | } 136 | ``` 137 | 138 | ## Retornando Tipo de Resultado 139 | 140 | Também podemos retornar tipos de resultados, como ```Ok``` e ```Err```: 141 | 142 | ```rust 143 | #[test] 144 | fn it_works() -> Result<(), String> { 145 | if 2 + 3 == 4 { 146 | Ok(()) 147 | } else { 148 | Err(String::from("two plus two does not equal four")) 149 | } 150 | } 151 | ``` 152 | 153 | Dentro de ```Err``` passamos a mensagem que será retornada nesse tipo. 154 | 155 | 156 | -------------------------------------------------------------------------------- /11 - Genéricos, Traits e Lifetime/01 - Tipos de Dados Genéricos.md: -------------------------------------------------------------------------------- 1 | # Tipos de Dados Genéricos 2 | 3 | Usamos genéricos para criar assinaturas para funções ou estruturas, onde podemos usar com diversos tipos de dados. 4 | 5 | ### Em Definições de Funções 6 | 7 | Quando definimos uma função que usa genéricos, nos colocamos os genéricos na assinatura da função, onde nos normalmente especificamos os tipos dos parâmetros e do retorno. Fazendo isso deixamos nosso código mais flexível e prevenimos repetição de código. 8 | 9 | Veja o código abaixo, duas funções iguais que encontram o maior valor num pedaço, vamos combinar essas duas funções usando genéricos. 10 | 11 | ```rust 12 | fn largest_i32(list: &[i32]) -> &i32 { 13 | let mut largest = &list[0]; 14 | 15 | for item in list { 16 | if item > largest { 17 | largest = item; 18 | } 19 | } 20 | 21 | largest 22 | } 23 | 24 | fn largest_char(list: &[char]) -> &char { 25 | let mut largest = &list[0]; 26 | 27 | for item in list { 28 | if item > largest { 29 | largest = item; 30 | } 31 | } 32 | 33 | largest 34 | } 35 | 36 | fn main() { 37 | let number_list = vec![34, 50, 25, 100, 65]; 38 | 39 | let result = largest_i32(&number_list); 40 | println!("The largest number is {}", result); 41 | 42 | let char_list = vec!['y', 'm', 'a', 'q']; 43 | 44 | let result = largest_char(&char_list); 45 | println!("The largest char is {}", result); 46 | } 47 | ``` 48 | 49 | Para transformar isso em uma única função, nomeamos o tipo do parâmetro, você pode usar qualquer nome que preferir, mas agora vamos usar ```T``` por convenção. 50 | 51 | ```rust 52 | fn largest(list: &[T]) -> &T { 53 | ``` 54 | 55 | A função ```largest``` é um genérico sobre o tipo ```T```, que recebe uma vetor de ```T``` e retorna o mesmo tipo ```T```. 56 | 57 | Esse código, agora, não funciona, mas vamos consertar mais tarde: 58 | 59 | ```rust 60 | fn largest(list: &[T]) -> &T { 61 | let mut largest = &list[0]; 62 | 63 | for item in list { 64 | if item > largest { 65 | largest = item; 66 | } 67 | } 68 | 69 | largest 70 | } 71 | 72 | fn main() { 73 | let number_list = vec![34, 50, 25, 100, 65]; 74 | 75 | let result = largest(&number_list); 76 | println!("The largest number is {}", result); 77 | 78 | let char_list = vec!['y', 'm', 'a', 'q']; 79 | 80 | let result = largest(&char_list); 81 | println!("The largest char is {}", result); 82 | } 83 | ``` 84 | 85 | Por enquanto, saiba que o erro ocorre porque tentamos comparar valores do tipo ```T```, mas só podemos fazer isso em valores que possam ser ordenados. Mais tarde, vamos consertar isso com traits. 86 | 87 | ### Em Estruturas 88 | 89 | Também podemos usar um parâmetro genérico em um ou mais campos usando a sintaxe ```<>```: 90 | 91 | ```rust 92 | struct Point { 93 | x: T, 94 | y: T, 95 | } 96 | 97 | fn main() { 98 | let integer = Point { x: 5, y: 10 }; 99 | let float = Point { x: 1.0, y: 4.0 }; 100 | } 101 | ``` 102 | 103 | Note que isso diz que os dois campos x e y são do mesmo tipo. Não funciona se tentarmos colocar um tipo diferente para cada. 104 | 105 | Para criarmos uma estrutura onde ```x``` e ```y``` são genéricos mas de diferentes tipos, podemos usar múltiplos parâmetros, assim: 106 | 107 | ```rust 108 | struct Point { 109 | x: T, 110 | y: U, 111 | } 112 | 113 | fn main() { 114 | let both_integer = Point { x: 5, y: 10 }; 115 | let both_float = Point { x: 1.0, y: 4.0 }; 116 | let integer_and_float = Point { x: 5, y: 4.0 }; 117 | } 118 | ``` 119 | 120 | ### Em Enumeradores 121 | 122 | Como fizemos em estruturas, podemos definir que os enumeradores tenham genéricos nas suas variantes, como funciona o enum ```Option```. Que retorna o tipo genérico ou nada. 123 | 124 | ```rust 125 | enum Option { 126 | Some(T), 127 | None, 128 | } 129 | ``` 130 | 131 | Quando você perceber situações no seu código com múltiplas definições de estruturas que apenas diferem nos tipos que elas seguram, você pode evitar a duplicação com tipos genéricos. 132 | 133 | ### Em Métodos 134 | 135 | ```rust 136 | struct Point { 137 | x: T, 138 | y: T, 139 | } 140 | 141 | impl Point { 142 | fn x(&self) -> &T { 143 | &self.x 144 | } 145 | } 146 | 147 | fn main() { 148 | let p = Point { x: 5, y: 10 }; 149 | 150 | println!("p.x = {}", p.x()); 151 | } 152 | ``` 153 | 154 | Definimos um método chamado x que retorna uma referência aos dados do campo x. -------------------------------------------------------------------------------- /06 - Structs/02 - Métodos Syntax.md: -------------------------------------------------------------------------------- 1 | # Métodos 2 | 3 | Métodos são parecidos com funções, nós declaramos eles com a palavra ```fn``` e um nome, eles podem ter parâmetros e retornar valores, e eles contém algum código que pode ser rodado quando alguém chamar esse método. Diferente de funções, métodos são definidos no contexto de uma _struct_, ou um Enum ou um Objeto Trait, o primeiro parâmetro deles sempre é ```self```, que representa a instância da estrutura onde o método está sendo chamado. 4 | 5 | Para aprender um pouco mais sobre métodos vamos usar esse código simples de exemplo: 6 | 7 | ```rust 8 | #[derive(Debug)] // a declaração permite imprimir informações de debug 9 | struct Rectangle { 10 | width: u32, 11 | height: u32, 12 | } 13 | 14 | fn main() { 15 | let rect1 = Rectangle { 16 | width: 30, 17 | height: 50, 18 | }; 19 | 20 | println!("rect1 is {:?}", rect1); 21 | 22 | println!("The area of the rectangle is {} square pixels",area(&rect1)); 23 | } 24 | 25 | fn area(rectangle: &Rectangle) -> u32 { 26 | rectangle.width * rectangle.height 27 | } 28 | ``` 29 | 30 | O código acima é um programa simples, que tem uma estrutura de um retângulo, recebe as informações de altura e largura e depois usa a função ```area``` para calcular o tamanho da área desse retângulo e imprimir na tela. 31 | 32 | ## Definindo Métodos 33 | 34 | Vamos mudar a função ```area``` que tem uma instância de ```Rectangle``` como parâmetro e, em vez disso, criar um método definido na estrutura: 35 | 36 | ```rust 37 | #[derive(Debug)] 38 | struct Rectangle { 39 | width: u32, 40 | height: u32, 41 | } 42 | 43 | impl Rectangle { 44 | fn area(&self) -> u32 { 45 | self.width * self.height 46 | } 47 | } 48 | 49 | fn main() { 50 | let rect1 = Rectangle { 51 | width: 30, 52 | height: 50, 53 | }; 54 | 55 | println!( 56 | "The area of the rectangle is {} square pixels.", 57 | rect1.area() 58 | ); 59 | } 60 | ``` 61 | 62 | Para criar essa função no escopo do ```Rectangle``` começamos um bloco de ```impl``` (implementação) para ```Rectangle```, tudo que está dentro dele será associado com o tipo ```Rectangle```. O primeiro parâmetro é self, ele mesmo, no caso, a instância de ```Rectangle```. 63 | 64 | Depois na ```main``` chamamos o método que agora está associado ao ```Rectangle``` como um método de ```rect1```. 65 | 66 | ### Métodos Com Mais Parâmetros 67 | 68 | Vamos implementar mais um método na _struct_ ```Rectangle```, agora um método que pega outra instância de ```Rectangle``` e retorna true se o segundo ```Rectangle``` consegue preencher totalmente o outro, se não, retorna falso. 69 | 70 | ```rust 71 | fn main() { 72 | let rect1 = Rectangle { 73 | width: 30, 74 | height: 50, 75 | }; 76 | let rect2 = Rectangle { 77 | width: 10, 78 | height: 40, 79 | }; 80 | let rect3 = Rectangle { 81 | width: 60, 82 | height: 45, 83 | }; 84 | 85 | println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2)); 86 | println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3)); 87 | } 88 | ``` 89 | 90 | Agora adicionamos esse outro método ```can_hold```, que recebe a referência a própria instância ```&self```, e a referência a outro objeto ```other: &Rectangle```, depois compara a sua própria instância com a outra que ela recebe, e retorna true se a largura e altura dela mesma é maior que a outra. 91 | 92 | ```rust 93 | impl Rectangle { 94 | fn area(&self) -> u32 { 95 | self.width * self.height 96 | } 97 | 98 | fn can_hold(&self, other: &Rectangle) -> bool { 99 | self.width > other.width && self.height > other.height 100 | } 101 | } 102 | ``` 103 | 104 | 105 | ### Funções Associadas 106 | 107 | Todas as funções definidas dentro de um block ```impl``` são chamadas de _funções associadas_ por que elas estão associadas com o tipo nomeado depois de ```impl```. Podemos definir _funções associadas_ que não _self_ como o primeiro parâmetro, porque elas não precisam de uma instância do tipo para trabalhar com ele. 108 | 109 | Funções associadas que não são métodos são geralmente usadas para construtores que vão retornar uma nova instância da _struct_. Por exemplo, vamos criar uma função que cria um novo retângulo do tipo quadrado agora, que recebe um mesmo valor e retorna uma instância com o mesmo valor na altura e largura: 110 | 111 | Para chamar essas funções associadas usamos a sintaxe ```::``` assim: 112 | 113 | ```rust 114 | impl Rectangle { 115 | fn square(size: u32) -> Self { 116 | Self { 117 | width: size, 118 | height: size, 119 | } 120 | } 121 | } 122 | 123 | fn main() { 124 | let squr1 = Rectangle::square(2); 125 | println!("{:?}", squr1); // Rectangle { width: 2, height: 2 } 126 | } 127 | ``` 128 | 129 | 130 | ### Múltiplos Blocos impl 131 | 132 | Podemos também criar blocos separados com ```impl```: 133 | 134 | ```rust 135 | impl Rectangle { 136 | fn area(&self) -> u32 { 137 | self.width * self.height 138 | } 139 | } 140 | 141 | impl Rectangle { 142 | fn can_hold(&self, other: &Rectangle) -> bool { 143 | self.width > other.width && self.height > other.height 144 | } 145 | } 146 | ``` 147 | 148 | Ambos estão associados com a _struct_ ```Rectangle```. 149 | 150 | -------------------------------------------------------------------------------- /06 - Structs/01 - Definindo e Instanciando Estruturas.md: -------------------------------------------------------------------------------- 1 | # O Que É 2 | 3 | _Para fins de aprendizado vamos chamar estruturas pelo seu nome técnico structs_ 4 | 5 | Uma _struct_ é um tipo de dado customizado, que permite você empacotar vários valores relacionados de um grupo. É parecido com os atributos de um objeto de uma linguagem POO. 6 | 7 | # Definindo e Instanciando Estruturas 8 | 9 | Structs são familiares com _tuples_, esses dois armazenam valores múltiplos, e como uma _tuple_, as partes de uma estrutura podem ter tipos diferentes, e diferente de uma _tupla_, em uma _struct_ você sempre vai nomear cada parte dos seus dados, então é claro o que aquele valor significa. 10 | 11 | Definimos uma _struct_ com a palavra ```struct``` e colocamos seus dados dentro de chaves ```{}```: 12 | 13 | ```rust 14 | struct User { 15 | active: bool, 16 | username: String, 17 | email: String, 18 | sign_in_count: u64, 19 | } 20 | ``` 21 | 22 | Definimos a _struct_, agora para usar temos que criar uma _instancia_ dela, especificando cada valor em cada campo dos dados. As chaves precisam ter os mesmos nomes que definimos na criação da _struct_. 23 | 24 | ```rust 25 | fn main() { 26 | let user1 = User { 27 | active: true, 28 | username: String::from("someusername123"), 29 | email: String::from("someone@example.com"), 30 | sign_in_count: 1, 31 | }; 32 | } 33 | ``` 34 | 35 | Para acessar um valor de uma instancia da estrutura usamos ```.```, por exemplo, para acessar a chave username: ```user1.username```. Se a instancia for mutável podemos acessar a chave e modificar assim também: 36 | 37 | ```rust 38 | fn main() { 39 | let mut user1 = User { 40 | active: true, 41 | username: String::from("someusername123"), 42 | email: String::from("someone@example.com"), 43 | sign_in_count: 1, 44 | }; 45 | 46 | user1.email = String::from("anotheremail@example.com"); 47 | } 48 | ``` 49 | 50 | Para mudar algo na instancia, toda ela precisa ser mutável, o Rust não nos permite deixar apenas alguns campos mutáveis e outros não. 51 | 52 | Podemos também criar uma função para criar novas instancias, aqui fazemos uma função que recebe um email, um nome e retorna uma instancia de ```User```: 53 | 54 | ```rust 55 | fn build_user(email: String, username: String) -> User { 56 | User { 57 | active: true, 58 | username: username, 59 | email: email, 60 | sign_in_count: 1, 61 | } 62 | } 63 | ``` 64 | 65 | _Quando os parâmetros da função e os campos da struct tiverem o mesmo nome, você pode simplesmente passar eles, sem ter que fazer ```username: username```_. 66 | 67 | ### Struct Update Syntax 68 | 69 | Geralmente é bem útil criar novas instancias de uma struct que contem a maioria dos valores de outra instância, mas muda alguns. Podemos fazer isso usando _struct update syntax_. 70 | 71 | ```rust 72 | fn main() { 73 | // --snip-- 74 | 75 | let user2 = User { 76 | active: user1.active, 77 | username: user1.username, 78 | email: String::from("another@example.com"), 79 | sign_in_count: user1.sign_in_count, 80 | }; 81 | } 82 | ``` 83 | 84 | Ou também podemos passar desestruturando, com ```..user1```, na função abaixo vamos usar todos os campos de ```user1```, menos o email, o email vamos criar o nosso próprio: 85 | 86 | ```rust 87 | fn main() { 88 | // --snip-- 89 | 90 | let user2 = User { 91 | email: String::from("another@example.com"), 92 | ..user1 93 | }; 94 | } 95 | ``` 96 | 97 | Lembre-se, os tipos dinâmicos, como String, ao serem associados tem seus donos movidos, logo, quando criamos o ```user2``` e desestruturamos ele passando o valor do campo username, username deixa de existir no ```user1```, mas os campos ```active``` e ```sign_in_count``` continuam existindo em ```user1``` pois são tipos literais têm a trait ```Copy```. 98 | 99 | ### Usando Tuple Structs Sem Campos Com Nome Para Criar Tipos Diferentes 100 | 101 | Rust também suporta structs parecidas com tuples, chamadas _tuple structs_. Tuple structs apenas tem seus tipos nos campos, não tem nomes para eles. São úteis quando você quer dar um nome a tupla toda e fazer ela ser um tipo diferente das outras tuplas. 102 | 103 | Para definirmos uma _tuple struct_ usamos a palavra ```struct``` seguido do seu nome e entre ```()``` os tipos que para ela: 104 | 105 | ```rust 106 | struct Color(i32, i32, i32); 107 | struct Point(i32, i32, i32); 108 | 109 | fn main() { 110 | let black = Color(0, 0, 0); 111 | let origin = Point(0, 0, 0); 112 | } 113 | ``` 114 | 115 | Cada _struct_ que você definir vai ter seu próprio tipo. Uma função que recebe um parâmetro do tipo ```Color``` não pode receber ```Point``` como argumento, mesmo que elas tenham os mesmos campos com as mesmos tipos, e você também pode acessar um valor específico em uma _tuple struct_ com ```black.0``` por exemplo. 116 | 117 | ### Unit-Like Structs Sem Campos 118 | 119 | Também podemos definir _structs_ sem campos, são chamadas de unit-like structs, porque elas são similar ao ```()```, elas são úteis quando você precisa implementar alguma característica em algum tipo mas não tem nenhum dado que você quer guardar no próprio tipo, vamos discutir mais à frente. 120 | 121 | ```rust 122 | struct AlwaysEqual; 123 | 124 | fn main() { 125 | let subject = AlwaysEqual; 126 | } 127 | ``` 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /04 - Conceitos comuns/02 - Tipos de Data.md: -------------------------------------------------------------------------------- 1 | # Tipos de Data 2 | 3 | Todo valor em Rust tem um tipo de data, isso nos ajuda a saber como trabalhar com esse tipo, vamos ver dois tipos de data: escalável e composta. 4 | 5 | O Rust é uma linguagem tipada estática, o que quer dizer que o compilador precisa saber o tipo de todas as variáveis na hora de compilar, o compilador geralmente consegue inferir o tipo de data que queremos usar baseado no valor atribuído e como usamos isso. 6 | 7 | Aqui, se não especificássemos que queríamos fazer o parse para um número, o compilador iria gerar um erro, pois ele precisa saber que tipo é para usar: 8 | 9 | ```rust 10 | let guess: u32 = "42".parse().expect("Not a number!"); 11 | ``` 12 | 13 | ## Tipos escaláveis 14 | 15 | Representa um valor único, o Rust tem quatro tipos primários escaláveis: Integers, Floating numbers, Booleans, e Characters. 16 | 17 | Não vou me aprofundar por aqui, mas saiba que tipos signed são para números negativos e positivos, com sinal, e números unsigned funcionam apenas em números positivos. 18 | 19 | #### Inteiros 20 | |Length|Signed|Unsigned| 21 | |---|---|---| 22 | |8-bit|`i8`|`u8`| 23 | |16-bit|`i16`|`u16`| 24 | |32-bit|`i32`|`u32`| 25 | |64-bit|`i64`|`u64`| 26 | |128-bit|`i128`|`u128`| 27 | |arch|`isize`|`usize`| 28 | 29 | #### Floating 30 | 31 | Em rust os tipos flutuantes são ```f32``` e ```f64``` para sistemas x32 e x64 bits respectivamente. O tipo padrão é ```f64``` para ter números com mais precisão. 32 | 33 | ```rust 34 | fn main() { 35 | let x = 2.0; // f64 36 | 37 | let y: f32 = 3.0; // f32 38 | } 39 | ``` 40 | 41 | 42 | ### Operações Numéricas 43 | 44 | Rust também suporta operações básicas de matemática: 45 | 46 | ```rust 47 | fn main() { 48 | // Adição 49 | let soma = 5 + 10; 50 | 51 | // Subtração 52 | let diferenca = 95.5 - 4.3; 53 | 54 | // Multiplicação 55 | let produto = 4 * 30; 56 | 57 | // Divisão 58 | let cociente = 56.7 / 32.2; 59 | let truncamento = -5 / 3; // Resulta -1 60 | 61 | // Restante 62 | let restante = 43 % 5; 63 | } 64 | ``` 65 | 66 | ### Booleanos 67 | 68 | O Rust só aceita ```true``` e ```false``` para valores booleanos:: 69 | 70 | ```rust 71 | fn main() { 72 | let t = true; 73 | 74 | let f: bool = false; 75 | } 76 | ``` 77 | 78 | ### Caractere 79 | 80 | No Rust, ```char``` são com single quotes ('), ao contrário de strings que usam double quotes ("). Ele é um tipo com quatro bytes em tamanho e representa um Valor Escalável Unicode. Quer dizer que aceita mais do que apenas ASCII, como letras chinesas, japonesas, coreanas, emojis, etc.. 81 | 82 | ```rust 83 | fn main() { 84 | let c = 'z'; 85 | let z: char = 'ℤ'; 86 | let heart_eyed_cat = '😻'; 87 | } 88 | ``` 89 | 90 | ## Tipos Compostos 91 | 92 | Tipos compostos podem agrupar múltiplos valores dentro de um tipo, o Rust tem dois tipos primitivos de tipos compostos: Tuplas e Arrays. 93 | 94 | ### Tipo Tupla 95 | 96 | Geralmente é uma forma de agrupar vários valores com uma variedade de tipos, além disso eles tem tamanho fixo, uma vez declarados, não podem crescer ou diminuir de tamanho. 97 | 98 | Quando criamos uma tupla cada posição dela terá um tipo: 99 | 100 | ```rust 101 | fn main() { 102 | let tup: (i32, f64, u8) = (500, 6.4, 1); 103 | } 104 | ``` 105 | 106 | Para desestruturar tuplas podemos fazer em ordem, assim: 107 | 108 | ```rust 109 | fn main() { 110 | let tup = (500, 6.4, 1); 111 | 112 | let (x, y, z) = tup; 113 | 114 | println!("O valor de y é: {y}"); 115 | } 116 | ``` 117 | 118 | Também podemos acessar um valor diretamente usando . e o índice que queremos, ex: ```x.0```: 119 | 120 | ```rust 121 | fn main() { 122 | let x: (i32, f64, u8) = (500, 6.4, 1); 123 | 124 | let five_hundred = x.0; 125 | 126 | let six_point_four = x.1; 127 | 128 | let one = x.2; 129 | } 130 | ``` 131 | 132 | Como a maioria das linguagens, o primeiro índice da tupla é o 0. 133 | 134 | 135 | 136 | ### Tipo Array 137 | 138 | O tipo array, diferente da tupla, tem que ter todos os elementos do mesmo tipo, e diferente de arrays em outras linguagens, arrays no Rust também devem ter tamanho fixo: 139 | 140 | ```rust 141 | fn main() { 142 | let a = [1, 2, 3, 4, 5]; 143 | } 144 | ``` 145 | 146 | Uma array não é flexível como um tipo _vetor_. Um _vetor_ é similar a uma coleção de tipos, provida da biblioteca standard, que permite crescer e diminuir de tamanho, vamos discutir mais a frente. 147 | 148 | Arrays são mais úteis quando sabemos que o número de elementos não vai mudar, como por exemplo, os meses de um ano: 149 | 150 | ```rust 151 | let meses = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", 152 | "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"]; 153 | ``` 154 | 155 | Podemos também criar um array especificando o tipo de elemento que ele vai conter e o seu tamanho: 156 | 157 | ```rust 158 | let a: [i32; 5] = [1, 2, 3, 4, 5]; 159 | 160 | // ou também o valor exato de cada elemento 161 | 162 | let b: [3, 5] = [3, 3, 3, 3, 3]; 163 | ``` 164 | 165 | Podemos usar o index para acessar elementos de um array, como: 166 | 167 | ```rust 168 | fn main() { 169 | let a = [1, 2, 3, 4, 5]; 170 | 171 | let primeiro = a[0]; 172 | let segundo = a[1]; 173 | } 174 | ``` 175 | 176 | Se tentarmos acessar um index inexistente o Rust previne problemas futuros e lança um erro na tela, depois termina o programa. 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ✋ Olá, sou o João Vitor. 2 | 3 | Sou desenvolvedor Typescript há algum tempo e recentemente tive vontade de aprender uma linguagem de programação nova, então escolhi _Rust_. 4 | 5 | Nesse repositório eu vou traduzir e simplificar o livro "The Rust Programming Language", que você pode [acessar aqui](https://doc.rust-lang.org/book), de acordo com o que entendo. Estou fazendo isso para reforçar o meu aprendizado e também para talvez ajudar alguém que consiga entender algum conceito de Rust com as minhas palavras. 6 | 7 | O repositório é aberto então você pode enviar sugestões de alteração 😀 8 | 9 | #### GUIA 10 | 11 | 1. [Introdução](https://github.com/jotavetech/the-rust-book/blob/main/01%20-%20Introdu%C3%A7%C3%A3o.md) 12 | 2. [Começando](https://github.com/jotavetech/the-rust-book/tree/main/02%20-%20Come%C3%A7ando) 13 | 1. [Hello, World!](https://github.com/jotavetech/the-rust-book/blob/main/02%20-%20Come%C3%A7ando/01%20-%20Hello%2C%20World!.md) 14 | 2. [Hello, Cargo!](https://github.com/jotavetech/the-rust-book/blob/main/02%20-%20Come%C3%A7ando/02%20-%20Hello%2C%20Cargo!.md) 15 | 3. [Jogo de adivinhação](https://github.com/jotavetech/the-rust-book/blob/main/03%20-%20Jogo%20de%20adivinha%C3%A7%C3%A3o/01%20-%20Jogo.md) 16 | 4. [Conceitos Comuns](https://github.com/jotavetech/the-rust-book/tree/main/04%20-%20Conceitos%20comuns) 17 | 1. [Variáveis e Mutabilidade](https://github.com/jotavetech/the-rust-book/blob/main/04%20-%20Conceitos%20comuns/01%20-%20Vari%C3%A1veis%20e%20Mutabilidade.md) 18 | 2. [Tipos de Data](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/04%20-%20Conceitos%20comuns/02%20-%20Tipos%20de%20Data.md) 19 | 3. [Funções](https://github.com/jotavetech/the-rust-book/blob/main/04%20-%20Conceitos%20comuns/03%20-%20Fun%C3%A7%C3%B5es.md) 20 | 4. [Controle de Fluxo](https://github.com/jotavetech/the-rust-book/blob/main/04%20-%20Conceitos%20comuns/04%20-%20Controle%20de%20Fluxo.md) 21 | 5. [Entendendo Ownership](https://github.com/jotavetech/the-rust-book/tree/main/05%20-%20Entendendo%20Ownership) 22 | 1. [O Que é Ownership](https://github.com/jotavetech/the-rust-book/blob/main/05%20-%20Entendendo%20Ownership/01%20-%20O%20Que%20%C3%A9%20Ownership.md) 23 | 2. [Referências e Empréstimos](https://github.com/jotavetech/the-rust-book/blob/main/05%20-%20Entendendo%20Ownership/02%20-%20Refer%C3%AAncias%20e%20Empr%C3%A9stimos.md) 24 | 6. [Estruturas](https://github.com/jotavetech/the-rust-book/tree/main/06%20-%20Structs) 25 | 1. [Definindo e Instanciando Estruturas](https://github.com/jotavetech/the-rust-book/blob/main/06%20-%20Structs/01%20-%20Definindo%20e%20Instanciando%20Estruturas.md) 26 | 2. [Métodos](https://github.com/jotavetech/the-rust-book/blob/main/06%20-%20Structs/02%20-%20M%C3%A9todos%20Syntax.md) 27 | 7. [Enumeradores](https://github.com/jotavetech/the-rust-book/tree/main/07%20-%20Enumeradores) 28 | 1. [Enumeradores e Pattern Matching](https://github.com/jotavetech/the-rust-book/blob/main/07%20-%20Enumeradores/01%20-%20Enumeradores%20e%20Pattern%20Matching.md) 29 | 2. [Construção de Controle de Fluxo](https://github.com/jotavetech/the-rust-book/blob/main/07%20-%20Enumeradores/02%20-%20Constru%C3%A7%C3%A3o%20de%20Controle%20de%20Fluxo.md) 30 | 8. [Packages, Crates e Módulos](https://github.com/jotavetech/the-rust-book-ptbr/tree/main/08%20-%20Packages%2C%20Crates%20e%20M%C3%B3dulos) 31 | 1. [Começo](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/08%20-%20Packages%2C%20Crates%20e%20M%C3%B3dulos/01%20-%20Come%C3%A7o.md) 32 | 2. [Packages e Crates](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/08%20-%20Packages%2C%20Crates%20e%20M%C3%B3dulos/02%20-%20Packages%20e%20Crates.md) 33 | 3. [Definindo Módulos para Controlar Escopo e Privacidade](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/08%20-%20Packages%2C%20Crates%20e%20M%C3%B3dulos/03%20-%20Definindo%20M%C3%B3dulos%20para%20Controlar%20Escopo%20e%20Privacidade.md) 34 | 9. [Coleções Comuns](https://github.com/jotavetech/the-rust-book-ptbr/tree/main/09%20-%20Cole%C3%A7%C3%B5es%20Comuns) 35 | 1. [Lista de Valores com Vetores](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/09%20-%20Cole%C3%A7%C3%B5es%20Comuns/01%20-%20Guardando%20Lista%20de%20Valores%20com%20Vetores.md) 36 | 2. [Texto UTF-8 com Strings](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/09%20-%20Cole%C3%A7%C3%B5es%20Comuns/02%20-%20Armazenando%20Texto%20UTF-8%20com%20Strings.md) 37 | 3. [Keys com Valores Associados em HashMap](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/09%20-%20Cole%C3%A7%C3%B5es%20Comuns/03%20-%20Armazenando%20Keys%20com%20Valores%20Associados%20em%20Hash%20Maps.md) 38 | 10. [Lidando com Erros](https://github.com/jotavetech/the-rust-book-ptbr/tree/main/10%20-%20Lidando%20Com%20Erro) 39 | 1. [Começo](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/10%20-%20Lidando%20Com%20Erro/01%20-%20Come%C3%A7o.md) 40 | 2. [Erros Irrecuperáveis com panic!](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/10%20-%20Lidando%20Com%20Erro/02%20-%20Erros%20Irrecuper%C3%A1veis%20com%20Panic!.md) 41 | 3. [Erros recuperáveis com result](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/10%20-%20Lidando%20Com%20Erro/03%20-%20Erros%20Recuper%C3%A1veis%20com%20Result.md) 42 | 11. [Genéricos, Traits e Lifetime](https://github.com/jotavetech/the-rust-book-ptbr/tree/main/11%20-%20Gen%C3%A9ricos%2C%20Traits%20e%20Lifetime) 43 | 1. [Tipos de Dados Genéricos](https://github.com/jotavetech/the-rust-book-ptbr/blob/main/11%20-%20Gen%C3%A9ricos%2C%20Traits%20e%20Lifetime/01%20-%20Tipos%20de%20Dados%20Gen%C3%A9ricos.md) 44 | -------------------------------------------------------------------------------- /04 - Conceitos comuns/04 - Controle de Fluxo.md: -------------------------------------------------------------------------------- 1 | # Controle de Fluxo 2 | 3 | A maioria das estruturas em Rust que vão te permitir controlar o fluxo do seu programa, se algo for verdadeiro ou falso, são as expressões ```if``` e loops. 4 | 5 | ### Expressões if 6 | 7 | Uma expressão ```if``` te permite fazer algo dependendo a condição que você passar pra ela, se estiver chovendo, não saia de casa, se não estiver chovendo, vá passear. 8 | 9 | ```rust 10 | let is_raining = false; 11 | 12 | if is_raining { // se estiver chovendo rode esse bloco de código: 13 | println!("Fique em casa!"); 14 | } else { // se não, rode este: 15 | println!("Vá passear"); 16 | } 17 | ``` 18 | 19 | Uma expressão ```if``` recebe uma condição para checar se ela é verdadeira, caso sim, ela roda o bloco de código. No nosso exemplo criamos uma variável is_raining e associamos ela a um valor booleano de _false_, depois verificamos: is_raining é verdadeiro? (true), se sim mostre na tela **Fique em casa**, caso contrário, mostre na tela **Vá passear***. 20 | 21 | Podemos fazer comparações com operadores lógicos também, como ```a == b```, que verifica se ```a``` é igual a ```b```. Outro exemplo ```a > b```, que verifica se ```a``` é maior que ```b```. 22 | 23 | Também podemos verificar condições múltiplas usando ```else if```: 24 | 25 | ```rust 26 | fn main() { 27 | let number = 6; 28 | 29 | if number % 4 == 0 { 30 | println!("number é divisivel por 4"); 31 | } else if number % 3 == 0 { 32 | println!("number é divisivel por 3"); 33 | } else if number % 2 == 0 { 34 | println!("number é divisivel por 2"); 35 | } else { 36 | println!("number é divisivel por 4, 3, ou 2"); 37 | } 38 | } 39 | ``` 40 | 41 | Também podemos usar a declaração ```if``` para retornar valores caso uma condição seja verdadeira e associar ele à variável: 42 | 43 | ```rust 44 | fn main() { 45 | let condition = true; 46 | // se a condição for verdadeira retorna 5, se não, retorna 6. 47 | let number = if condition { 5 } else { 6 }; 48 | 49 | println!("O valor de number é: {number}"); 50 | } 51 | ``` 52 | 53 | Se tentarmos retornar um número no ```if``` e uma string no ```else``` vai gerar um erro, porque variáveis só podem ter um tipo e o compilador precisa saber qual é esse tipo na hora de compilar o código. 54 | 55 | ```rust 56 | fn main() { 57 | let condition = true; 58 | 59 | let number = if condition { 5 } else { "six" }; 60 | 61 | println!("The value of number is: {number}"); 62 | } 63 | ``` 64 | 65 | 66 | ### Repetição com Loops 67 | 68 | A palavra ```loop``` diz ao Rust para executar um bloco de código até você pedir explicitamente para parar. 69 | 70 | No exemplo abaixo, o código será executado infinitamente, até pararmos ele manualmente: 71 | 72 | ```rust 73 | fn main() { 74 | loop { 75 | println!("de novo!"); 76 | } 77 | } 78 | ``` 79 | 80 | Para poder parar um laço de repetição ```loop``` usamos a palavra ```break``` e podemos passar o valor a ser retornado depois dessa palavra: 81 | 82 | ```rust 83 | fn main() { 84 | let mut counter = 0; 85 | 86 | let result = loop { 87 | counter += 1; 88 | 89 | if counter == 10 { 90 | break counter * 2; 91 | } 92 | }; 93 | 94 | println!("O resultado é {result}"); 95 | } 96 | ``` 97 | 98 | Na função acima criamos um loop que vai aumentar o valor de ```counter``` toda vez que passar, e sempre que rodar o bloco de código ele vai verificar se ```counter``` é 10, se sim, vai parar o loop com a palavra ```break``` e retornar o valor de ```counter``` multiplicado por 2 (10 x 2 = 20). Agora a variável result vai ter o valor 20 associado a ela. 99 | 100 | #### Loops Condicionais com While 101 | 102 | Um programa geralmente precisa de uma condição para continuar com um loop, enquanto a condição for verdadeira o loop roda. E quando a condição deixa de ser verdadeira o loop para. 103 | 104 | ```rust 105 | fn main() { 106 | let mut number = 3; 107 | 108 | while number != 0 { 109 | println!("{number}!"); 110 | 111 | number -= 1; 112 | } 113 | 114 | println!("Saiu do loop!!!"); 115 | } 116 | ``` 117 | 118 | No loop ```while``` a cima passamos a condição de que se ```number``` for diferente de zero o bloco de código será rodado, então para podermos parar o loop, toda vez que ela passa no bloco de código diminuímos o valor de ```number``` em -1 até chegar a 0 e parar o loop. Enquanto a condição for ```true``` o loop roda, quando deixar de ser ```true``` o loop para. 119 | 120 | Outra alternativa para executar loops e algum código em cada item de uma coleção, por exemplo, é o ```for``` loop. 121 | 122 | ```rust 123 | fn main() { 124 | let a = [10, 20, 30, 40, 50]; 125 | 126 | for element in a { 127 | println!("O valor do elemento é: {element}"); 128 | } 129 | } 130 | ``` 131 | 132 | O código acima passa uma coleção de números e o que ele faz e passar por todos os números dessa coleção e associar cada index que passa em ordem, na primeira vez que passar ```element``` será o valor do index 0 de ```a``` (10), na segunda vez que passar, ```element``` será o valor do index 1 de ```a``` (20), e por assim vai.. 133 | 134 | Ele é mais seguro e consistente, pois cuida sozinho, por exemplo, do tamanho de um array e sabe exatamente quantas vezes temos que passar. 135 | 136 | Também podemos passar um range de valores, assim: 137 | 138 | ```rust 139 | fn main() { 140 | for number in (1..4) { 141 | println!("{number}!"); 142 | } 143 | println!("Saiu do loop!!!"); 144 | } 145 | ``` 146 | 147 | -------------------------------------------------------------------------------- /07 - Enumeradores/02 - Construção de Controle de Fluxo.md: -------------------------------------------------------------------------------- 1 | # Construção de Controle de Fluxo match 2 | 3 | Rust tem um controle de fluxo chamado ```match``` que permite você comparar um valor com uma série de padrões e executar um código com base no padrão correspondente. 4 | 5 | Padrões ```patterns``` podem ser feitos por valores literais, nomes de variáveis, e outras coisas. 6 | 7 | Pense que uma expressão ```match``` é como uma máquina de sortear moedas: 8 | 9 | ```rust 10 | enum Coin { 11 | Penny, 12 | Nickel, 13 | Dime, 14 | Quarter, 15 | } 16 | 17 | fn value_in_cents(coin: Coin) -> u8 { 18 | match coin { 19 | Coin::Penny => 1, 20 | Coin::Nickel => 5, 21 | Coin::Dime => 10, 22 | Coin::Quarter => 25, 23 | } 24 | } 25 | ``` 26 | 27 | Primeiros chamamos a palavra ```match``` seguida de uma expressão, que é o valor ```coin```. É bem parecido com o ```if```, mas tem uma diferença: com o ```if```, a condição precisa avaliar um valor booleano, mas aqui pode ser qualquer tipo. O tipo de ```coin``` nesse exemplo é o que definimos no Enum. 28 | 29 | Agora nos braços do ```match```. Um braço tem duas partes, um padrão e um código, o primeiro padrão é o valor que espera ```Coin::Penny``` e depois vem o operador ```=>``` que separa o padrão do código para rodar, nesse caso, o código apenas retorna 1, caso seja do padrão Penny. 30 | 31 | Quando uma expressão ```match``` executa ele compara o valor com o padrão de cada braço, em ordem. Se um pattern encontra o valor, ele executa o código, se não, continua procurando. 32 | 33 | Como era apenas um retorno o que estávamos fazendo não usamos {}, mas podemos escrever múltiplas linhas de código também: 34 | 35 | ```rust 36 | fn value_in_cents(coin: Coin) -> u8 { 37 | match coin { 38 | Coin::Penny => { 39 | println!("Lucky penny!"); 40 | 1 41 | } 42 | Coin::Nickel => 5, 43 | Coin::Dime => 10, 44 | Coin::Quarter => 25, 45 | } 46 | } 47 | ``` 48 | 49 | 50 | ## Patterns Que Vinculam a Valores 51 | 52 | Outra feature útil dos braços do match é que eles podem vincular as partes dos valores que correspondem ao padrão. 53 | 54 | Vamos mudar a variante ```Quarter``` para incluir algum valor guardado dentro de ```UsState```: 55 | 56 | ```rust 57 | enum UsState { 58 | Alabama, 59 | Alaska, 60 | // --snip-- 61 | } 62 | 63 | enum Coin { 64 | Penny, 65 | Nickel, 66 | Dime, 67 | Quarter(UsState), 68 | } 69 | 70 | fn main() {} 71 | ``` 72 | 73 | Quando um ```Coin::Quarter``` corresponde, a variável ```state``` vai estar vinculada ao valor do estado de quarters, então podemos usar o ```state``` nos braços: 74 | 75 | ```rust 76 | fn value_in_cents(coin: Coin) -> u8 { 77 | match coin { 78 | Coin::Penny => 1, 79 | Coin::Nickel => 5, 80 | Coin::Dime => 10, 81 | Coin::Quarter(state) => { 82 | println!("State quarter from {:?}!", state); 83 | 25 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | 90 | ## Matching com Option 91 | 92 | Podemos manusear o Option, visto na seção anterior, usando ```match```, como fizemos com o enum de ```coin```. Ao invés de comparar coins, vamos comparar as variantes de ```Option```. 93 | 94 | Vamos criar uma função que pega um ```Option```, e se tiver algum valor, atribui 1 para ele, se não retorna o valor de ```None``` e não faz nenhuma ação. 95 | 96 | ```rust 97 | fn plus_one(x: Option) -> Option { 98 | match x { 99 | None => None, 100 | Some(i) => Some(i + 1), 101 | } 102 | } 103 | 104 | let five = Some(5); 105 | let six = plus_one(five); 106 | let none = plus_one(None); 107 | ``` 108 | 109 | 110 | ## Matches São Exaustivos 111 | 112 | Os braços de um ```match``` tem que cobrir todas as possibilidades. 113 | 114 | Por exemplo, no código aqui, a falta de cobrir o pattern ```None``` vai gerar um erro e não vai compilar: 115 | 116 | ```rust 117 | fn plus_one(x: Option) -> Option { 118 | match x { 119 | Some(i) => Some(i + 1), 120 | } 121 | } 122 | ``` 123 | 124 | O Rust sabe que não cobrimos todas as possibilidades, e esquecemos um padrão, isso nos previne de ter um valor não tratado. 125 | 126 | 127 | ### Padrões Abrangentes e Espaços _ Reservados. 128 | 129 | Digamos que queremos rolar um dado, caso caia 3 ele faz algo, se cair 7 ele faz outra coisa, e se cair qualquer outro número ele faz outra coisa, teríamos que cobrir todo o resto de números para fazer a mesma coisa, porém podemos fazer assim: 130 | 131 | ```rust 132 | let dice_roll = 9; 133 | match dice_roll { 134 | 3 => add_fancy_hat(), 135 | 7 => remove_fancy_hat(), 136 | other => move_player(other), 137 | } 138 | 139 | fn add_fancy_hat() {} 140 | fn remove_fancy_hat() {} 141 | fn move_player(num_spaces: u8) {} 142 | ``` 143 | 144 | Assim, colocando no último braço, cobrimos todas as opções que o dado pode cair, porque o último padrão vai corresponder com todos os valores não especificados. Temos que fazer isso sempre no último braço porque o match verifica os valores em ordem. Logo, se não for 3 nem 7, vai ser o último. 145 | 146 | No pattern ```other```, pegamos o valor do dado para passar na função ```move_player```, mas caso não queiramos fazer nada com esse valor e apenas chamar uma função, podemos usar o ```_```. 147 | 148 | ```rust 149 | let dice_roll = 9; 150 | match dice_roll { 151 | 3 => add_fancy_hat(), 152 | 7 => remove_fancy_hat(), 153 | _ => reroll(), 154 | } 155 | 156 | fn add_fancy_hat() {} 157 | fn remove_fancy_hat() {} 158 | fn reroll() {} 159 | ``` 160 | 161 | -------------------------------------------------------------------------------- /05 - Entendendo Ownership/02 - Referências e Empréstimos.md: -------------------------------------------------------------------------------- 1 | # Referências e Empréstimos 2 | 3 | Para passarmos uma variável para uma função, sem mover e perder ela, podemos usar uma referência, que funciona como um ponteiro com um endereço, assim podemos acessar os dados armazenados nesse endereço. Diferente de um ponteiro, a referência garante que estará sempre apontando para um valor valido de um tipo específico. 4 | 5 | Usando a referência a um objeto: 6 | 7 | ```rust 8 | fn main() { 9 | let s1 = String::from("hello"); 10 | 11 | let len = calculate_length(&s1); 12 | 13 | println!("O tamanho de '{}' é {}.", s1, len); 14 | } 15 | 16 | fn calculate_length(s: &String) -> usize { 17 | s.len() 18 | } 19 | ``` 20 | 21 | A variável ```s``` de ```calculate_length``` aponta para o endereço de ```s1``` que aponta para o endereço com o valor na memória. 22 | 23 | O contrário de uma referência ```&``` é a desreferencia, usando o operador ```*```. 24 | 25 | A sintaxe ```&s1``` nos permite criar uma referência que se refere ao valor de ```s1``` mas não vira dono dela, os valores que está apontando não vão ser droppados quando a referência deixar de ser usada. 26 | 27 | A assinatura da função usa ```&``` para indicar que o tipo do parâmetro ```s``` é uma referência. 28 | 29 | ```rust 30 | fn calculate_length(s: &String) -> usize { // s é uma referência a uma String 31 | s.len() 32 | } // Aqui s pode sair do escopo, porque s é uma referência, ele não vai ser 33 | // droppado. 34 | ``` 35 | 36 | Quando fazemos uma referência estamos fazendo um **empréstimo**, então a função que pega a referência não vira a nova dona dela, logo, não tem como você pegar de volta, pois continua sendo seu. Se você, por exemplo, empresta sua caneta para uma pessoa, a pessoa vai usar a caneta para a função que ela precisa e depois vai voltar pra você, você não deixou de ser dono. 37 | 38 | Em um empréstimo não podemos modificar algo que só temos a referência, vai gerar erro: 39 | 40 | ```rust 41 | fn main() { 42 | let s = String::from("hello"); 43 | 44 | change(&s); 45 | } 46 | 47 | fn change(some_string: &String) { 48 | some_string.push_str(", world"); 49 | } 50 | ``` 51 | 52 | 53 | 54 | ### Referências Mutáveis 55 | 56 | Podemos arrumar o código acima e permitir a modificação de um valor emprestado, usando referências mutáveis: 57 | 58 | ```rust 59 | fn main() { 60 | let mut s = String::from("hello"); 61 | 62 | change(&mut s); 63 | } 64 | 65 | fn change(some_string: &mut String) { 66 | some_string.push_str(", world"); 67 | } 68 | ``` 69 | 70 | Agora a referência que criamos vai mudar também a própria variável, no caso, ```s``` agora tem o valor de uma string "_hello, world_". 71 | 72 | Uma referência mutável só pode referenciar a um valor, esse código tentando criar duas referências mutáveis vai falhar: 73 | 74 | ```rust 75 | let mut s = String::from("hello"); 76 | 77 | let r1 = &mut s; 78 | let r2 = &mut s; 79 | 80 | println!("{}, {}", r1, r2); 81 | ``` 82 | 83 | O rust previne corridas de dados na hora de compilar, isso acontece quando esses comportamentos ocorrem: 84 | 85 | - Dois ou mais ponteiros acessam o mesma data ao mesmo tempo. 86 | - Pelo menos um desses ponteiros está sendo usado para escrever na data. 87 | - Não tem nenhum mecanismo sendo usado para sincronizar o acesso a data. 88 | 89 | Seguindo a regra das variáveis serem droppadas quando saírem do escopo podemos fazer mais de uma referência assim: 90 | 91 | ```rust 92 | let mut s = String::from("hello"); 93 | 94 | { 95 | let r1 = &mut s; 96 | } // r1 sai do escopo e pode ser referênciado novamente sem problemas. 97 | 98 | let r2 = &mut s; 99 | ``` 100 | 101 | Também não podemos ter referências mutáveis enquanto tivemos alguma referência imutável para o mesmo valor assim: 102 | 103 | ```rust 104 | let mut s = String::from("hello"); 105 | 106 | let r1 = &s; // sem problema 107 | let r2 = &s; // sem problema 108 | let r3 = &mut s; // grande problema 109 | 110 | println!("{}, {}, and {}", r1, r2, r3); 111 | ``` 112 | 113 | Mas o código assim funciona: 114 | 115 | ```rust 116 | let mut s = String::from("hello"); 117 | 118 | let r1 = &s; // sem problema 119 | let r2 = &s; // sem problema 120 | println!("{} and {}", r1, r2); 121 | // as variaveis r1 e r2 nao serao usadas depois 122 | 123 | let r3 = &mut s; // sem problema, agora 124 | println!("{}", r3); 125 | ``` 126 | 127 | Porque ```r1``` e ```r2``` já foram usados antes de criar a próxima referência. 128 | 129 | 130 | ### Referências Pendentes 131 | 132 | Uma referência pendente é um ponteiro que referencia a um lugar na memória que pode ter sido dado por outra pessoa, em Rust, o compilador garante que aquela referência nunca vai ser pendente. Se você tem uma referência a alguma data, o compilador vai garantir que a data não vai sair do escopo antes da referência aos dados: 133 | 134 | ```rust 135 | fn main() { 136 | let reference_to_nothing = dangle(); 137 | } 138 | 139 | fn dangle() -> &String { 140 | let s = String::from("hello"); 141 | 142 | &s 143 | } 144 | ``` 145 | 146 | Vai retornar um erro: 147 | 148 | ```console 149 | this function's return type contains a borrowed value, but there is no value 150 | for it to be borrowed from 151 | ``` 152 | 153 | Isso se da porque a função cria uma nova string dentro do seu escopo e tenta retornar uma referência àquela string. Porém, como sabemos, assim que a função acabar, as valores do seu escopo vão ser droppados. Logo, aquela referência que foi criada diretamente dentro da função, deixará de existir. 154 | 155 | A solução aqui seria retornar uma String diretamente: 156 | 157 | ```rust 158 | fn no_dangle() -> String { 159 | let s = String::from("hello"); 160 | 161 | s 162 | } 163 | ``` 164 | 165 | -------------------------------------------------------------------------------- /09 - Coleções Comuns/02 - Armazenando Texto UTF-8 com Strings.md: -------------------------------------------------------------------------------- 1 | 2 | # Armazenando Texto UTF-8 com Strings 3 | 4 | Discutimos Strings no contexto de coleções pois são implementadas como conjuntos de bytes, e mais alguns métodos que dão a elas funcionalidades úteis quando esses bytes são interpretados como textos. 5 | 6 | ## O Que é Uma String? 7 | 8 | Strings são conjuntos de bits, que são mapeados para caracteres. Atualmente, para codificar bits para caracteres usamos o esquema UTF-8 (8-bit Unicode Transformation Format), onde cada caractere por ser representado por 1 até 4 bytes, em cada byte cabem 8 bits, com essa codificação, podemos ter mais de 1 milhão de caracteres representados. 9 | 10 | Em Rust temos apenas um tipo de string no core da linguagem, o ```str``` que é geralmente visto da sua forma emprestada ```&str```. Por ter tamanho dinâmico, seus bytes ficam armazenados na memória heap, e na memória stack temos uma referência onde aponta para o primeiro caractere da string na memória heap e o seu tamanho. 11 | 12 | Quando criamos uma string, passando diretamente o conteúdo dela, da forma abaixo, criamos uma string que será armazenada diretamente nos binários da aplicação, pois ela não muda e o compilador sabe qual o seu conteúdo e tamanho durante o tempo de compilação: 13 | 14 | ```rust 15 | let binaries_string = "armazenada nos binarios"; 16 | ``` 17 | 18 | Já o tipo ```String```, provido pela biblioteca padrão do Rust, é um tipo de string controlável, escalável, e mutável. Sempre vai ser armazenada na memória heap, e diferente de uma string slice ```(&str)```, agora referenciamos a 3 valores: O primeiro byte da string, o tamanho da string e a sua capacidade. 19 | 20 | ## Criando uma Nova String 21 | 22 | ```rust 23 | let mut s = String::new(); 24 | ``` 25 | 26 | Nessa linha criamos uma string vazia chamada ```s```, na qual podemos colocar dados dentro. Também podemos criar uma nova string com conteúdo vindo de uma string literal, que era armazenada nos binários, de dois modos: 27 | 28 | ```rust 29 | let data = "conteudo inicial"; 30 | 31 | let s = data.to_string(); 32 | let s = "conteudo inicial".to_string(); 33 | 34 | let s = String::from("conteudo inicial"); 35 | ``` 36 | 37 | ## Atualizando uma String 38 | 39 | Uma ```String``` pode crescer e o seu conteúdo pode mudar, como um ```Vec```: 40 | 41 | ```rust 42 | let mut s = String::from("foo"); 43 | s.push_str("bar"); 44 | // s = foobar 45 | ``` 46 | 47 | Também podemos colocar um único caractere com o ```push```: 48 | 49 | ```rust 50 | let mut s = String::from("lo"); 51 | s.push('l'); 52 | // s = lol 53 | ``` 54 | 55 | ### Concatenação com Operador + 56 | 57 | Passando o valor de ```s1``` sem passar a referência, como usamos no ```&s2``` torna o ```s1``` inacessível novamente, pois teve seu dono mudado e não existe mais, ao concatenar strings em Rust usando o operador "+", a propriedade da primeira string é transferida para a operação de concatenação. 58 | 59 | ```rust 60 | let s1 = String::from("Hello, "); 61 | let s2 = String::from("world!"); 62 | let s3 = s1 + &s2; // s1 foi movido e não existe mais 63 | ``` 64 | 65 | ## Index em Strings 66 | 67 | Em outras linguagens, acessar caracteres individuais em uma string usando seu index é uma operação comum, mas em Rust, você vai receber um erro. 68 | 69 | ```rust 70 | let s1 = String::from("hello"); 71 | let h = s1[0]; 72 | ``` 73 | 74 | Mas por quê? Vamos entender como o Rust armazena as Strings na memória. 75 | 76 | ### Representação Interna 77 | 78 | ```rust 79 | let hello = String::from("Hola"); 80 | ``` 81 | 82 | Quando olhamos ao tamanho da String, sabemos que ela será armazenadas com 4 bytes, pois cada letra usa 1 byte quando codificada com UTF-8. 83 | 84 | Porém, agora: 85 | 86 | ```rust 87 | let hello = String::from("Здравствуйте"); 88 | ``` 89 | 90 | Você pode pensar que temos 12 bytes nessa string, mas não, agora ela possuí 24 bytes. Porque agora, com o codificador do UTF-8, cada caractere da string possuí 2 bytes. 91 | 92 | Considere esse código inválido em Rust: 93 | 94 | ```rust 95 | let hello = "Здравствуйте"; 96 | let answer = &hello[0]; 97 | ``` 98 | 99 | Sabemos agora que a resposta não vai ser 3, a primeira letra. Quando codificamos com UTF-8 o primeiro byte, que representa o 3 é 208 e o segundo é 151, (208 151). A resposta poderia ser 208, mas apenas 208 não é um caractere válido, e provavelmente não é o que um usuário iria querer se ele estivesse procurando pela primeira letra de uma string. 100 | 101 | ### Fatiando Strings 102 | 103 | Se você realmente precisar criar uma fatia de uma string, o Rust vai pedir para ser mais específico. Ao invés de usar ```[]``` para pegar um index da string, você pode usar ```[]``` com uma distancia para pegar bytes específicos: 104 | 105 | ```rust 106 | let hello = "Здравствуйте"; 107 | 108 | let s = &hello[0..4]; 109 | ``` 110 | 111 | Assim, temos uma ```&str``` que terá os primeiros 4 bytes da string: Зд. 112 | 113 | Se tentarmos pegar apenas uma parte do caractere ```&hello[0..1]```, o Rust vai dar panico, pois não é um caractere válido. 114 | 115 | ### Métodos para Iterar sobre Strings 116 | 117 | A melhor forma de operar sobre partes de uma strings é especificar que você quer bytes ou caracteres, para retornar os caracteres com ```char```: 118 | 119 | ```rust 120 | for c in "Зд".chars() { 121 | println!("{c}"); 122 | } 123 | ``` 124 | 125 | Esse código vai imprimir o seguinte: 126 | 127 | ``` 128 | З 129 | д 130 | ``` 131 | 132 | Se você quiser retornar os bytes dos caracteres, pode usar o método ```bytes```: 133 | 134 | ```rust 135 | for b in "Зд".bytes() { 136 | println!("{b}"); 137 | } 138 | ``` 139 | 140 | Que vai imprimir: 141 | 142 | ```208 143 | 151 144 | 208 145 | 180 146 | ``` 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /07 - Enumeradores/01 - Enumeradores e Pattern Matching.md: -------------------------------------------------------------------------------- 1 | Enumeradores se referem a _enums_. Eles permitem você a definir um tipo enumerando suas possíveis variantes. 2 | 3 | ## Definindo um Enum 4 | 5 | Um _enum_ te da a possibilidade de dizer que o valor é um do set de valores do enum, por exemplo, talvez podemos querer dizer que um ```Rectangle``` é um set de possibilidades que também incluí ```Circle``` e ```Triangle```, para fazer isso, o Rust nos permite codificar essas possibilidades como um enum. 6 | 7 | Por exemplo, vamos criar um _enum_ de um IP Address, que pode ser ipv4 ou ipv6: 8 | 9 | ```rust 10 | enum IpAddrKind { 11 | V4, 12 | V6, 13 | } 14 | ``` 15 | 16 | Agora ```IpAddrKind``` é um tipo customizado que pode ser usado em qualquer lugar do código. 17 | 18 | 19 | ### Valores Enums 20 | 21 | Agora podemos criar instancias de cada tipo do _enum_ assim: 22 | 23 | ```rust 24 | let four = IpAddrKind::V4; 25 | let six = IpAddrKind::V6; 26 | ``` 27 | 28 | Também podemos criar uma função que recebe qualquer tipo de ```IpAddrKind```: 29 | 30 | ```rust 31 | fn route(ip_kind: IpAddrKind) {} 32 | ``` 33 | 34 | E podemos chamar essa função passando qualquer tipo de variante: 35 | 36 | ```rust 37 | route(IpAddrKind::V4); 38 | route(IpAddrKind::V6); 39 | ``` 40 | 41 | Usar _enums_ tem mais vantagens, pense mais sobre o tipo de endereço ip, no momento não temos uma maneira de guardar os dados, apenas sabemos qual é o tipo dele _v6_ ou _v4_. Podemos enfrentar esse problema usando _structs_: 42 | 43 | ```rust 44 | enum IpAddrKind { 45 | V4, 46 | V6, 47 | } 48 | 49 | struct IpAddr { 50 | kind: IpAddrKind, 51 | address: String, 52 | } 53 | 54 | let home = IpAddr { 55 | kind: IpAddrKind::V4, 56 | address: String::from("127.0.0.1"), 57 | }; 58 | 59 | let loopback = IpAddr { 60 | kind: IpAddrKind::V6, 61 | address: String::from("::1"), 62 | }; 63 | 64 | ``` 65 | 66 | 67 | Assim criamos uma _struct_ que recebe um tipo do enum ```IpAddrKing``` e também um campo ```adress``` do tipo ```String```, e depois instanciamos eles associando a cada tipo de dado. 68 | 69 | Podemos representar esse mesmo tipo apenas um ```enum```, melhor que um ```enum``` dentro de uma ```struct```, podemos colocar os dados diretamente dentro de uma variante do enum: 70 | 71 | ```rust 72 | enum IpAddr { 73 | V4(String), 74 | V6(String), 75 | } 76 | 77 | let home = IpAddr::V4(String::from("127.0.0.1")); 78 | 79 | let loopback = IpAddr::V6(String::from("::1")); 80 | ``` 81 | 82 | Podemos observar que o mesmo nome de cada variante do _enum_ também vira uma função construtura para uma instancia dele, quando chamamos ```IpAddr:V4()``` estamos fazendo uma chamada de função que pega um argumento e retorna uma instancia daquele tipo. 83 | 84 | Cada variante de um _enum_ pode ter dados de diferentes tipos e quantidades, como: 85 | 86 | ```rust 87 | enum IpAddr { 88 | V4(u8, u8, u8, u8), 89 | V6(String), 90 | } 91 | 92 | let home = IpAddr::V4(127, 0, 0, 1); 93 | 94 | let loopback = IpAddr::V6(String::from("::1")); 95 | ``` 96 | 97 | Criando outros tipos de variedades nas suas variantes: 98 | 99 | ```rust 100 | enum Message { 101 | Quit, 102 | Move { x: i32, y: i32 }, 103 | Write(String), 104 | ChangeColor(i32, i32, i32), 105 | } 106 | ``` 107 | 108 | - Quit: não tem data associada. 109 | - Move: tem campos nomeados, com chaves. 110 | - Write: tem uma String 111 | - ChangeColor: incluí 3 valores i32. 112 | 113 | Definir um enum com variantes é similar a criar diferentes tipos de _structs_, assim: 114 | 115 | ```rust 116 | struct QuitMessage; // unit struct 117 | struct MoveMessage { 118 | x: i32, 119 | y: i32, 120 | } 121 | struct WriteMessage(String); // tuple struct 122 | struct ChangeColorMessage(i32, i32, i32); // tuple struct 123 | ``` 124 | 125 | Mas usando tipos diferentes de _structs_ cada um teria seu próprio tipo, não poderíamos criar facilmente uma função que aceite qualquer tipo dessas mensagens como podemos criar com o enum ```Message```, que é definido como um tipo só. 126 | 127 | Também podemos definir métodos: 128 | 129 | ```rust 130 | impl Message { 131 | fn call(&self) { 132 | // method body would be defined here 133 | } 134 | } 135 | 136 | let m = Message::Write(String::from("hello")); 137 | m.call(); 138 | ``` 139 | 140 | 141 | ### O Enum Option e as Vantages Dele em Valores Nulos 142 | 143 | O enum ```Option``` codifica todo cenário comum onde algum valor pode ser algo ou pode ser nada. 144 | 145 | Por exemplo, se você pegar o primeiro item de uma lista não vazia você terá o valor, se ela for vazia, você não vai ter nada. 146 | 147 | O Rust não tem o valor _null_ como a maioria das linguagens, eles podem sempre ser um dos valores: null ou not-null. 148 | 149 | Temos um enum especial no Rust que lida com o conceito de um valor presente ou ausente, o ```Option```, e é definido pela biblioteca standard: 150 | 151 | ```rust 152 | enum Option { 153 | None, 154 | Some(T), 155 | } 156 | ``` 157 | 158 | Você não precisa trazer isso explicitamente para o código, podemos usar ```Some``` e ```None``` diretamente sem o prefixo ```Option```, O ```Option``` continua sendo um enum normal e ```None``` e ```Some``` são suas variantes. 159 | 160 | A sintaxe ```T``` vai ser conversada mais pra frente, porém por agora você precisa saber que ela representa um tipo genérico, que aceita qualquer tipo de dado. 161 | 162 | Usando valores de ```Option``` para segurar tipos de números e strings: 163 | 164 | ```rust 165 | let some_number = Some(5); 166 | let some_char = Some('e'); 167 | 168 | let absent_number: Option = None; 169 | ``` 170 | 171 | Quando temos um valor em ```Some``` quer dizer que temos algo, quando temos um valor ```None``` significa que temos algo com nulo, não temos um valor válido. 172 | 173 | Resumidamente, porque ```Option``` e ```T``` são tipos diferentes, o compilador não nos deixa usar o valor ```Option``` como um valor valido. 174 | 175 | Por exemplo, esse código não vai compilar: 176 | 177 | ```rust 178 | let x: i8 = 5; 179 | let y: Option = Some(5); 180 | 181 | let sum = x + y; 182 | ``` 183 | 184 | Isso acontece porque o compilador não entende como adicionar esses dois valores juntos, já que representam tipos diferentes, o compilador sempre vai garantis que temos um valor válido. 185 | 186 | Você tem que converter um ```Option``` para um ```T``` antes de fazer operações com ele. 187 | 188 | Para usar esse valor o _enum_ tem muitos métodos que você pode encontrar [nessa documentação](https://doc.rust-lang.org/std/option/enum.Option.html). 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /03 - Jogo de adivinhação/01 - Jogo.md: -------------------------------------------------------------------------------- 1 | # Jogo de Adivinhação 2 | 3 | ## Juntando Um Pouquinho de Tudo 4 | 5 | Agora vamos criar um jogo de adivinhação juntos, para aprender um pouco mais sobre variáveis, métodos, etc.. Vamos ver detalhadamente mais tarde, por agora vamos apenas praticar os fundamentos. 6 | 7 | Vamos criar um programa básico que vai gerar um número inteiro entre 1 e 100 e vai pedir para o usuário digitar um chute, depois vai dizer se o chute está alto ou baixo, se for o chute correto vai ser impresso parabéns no terminal e terminar o programa. 8 | 9 | Para começar vamos criar um novo projeto com Cargo, como você já aprendeu anteriormente. 10 | 11 | ```console 12 | $ cargo new guessing_game 13 | $ cd guessing_game 14 | ``` 15 | 16 | E agora dentro do diretório _src/main.rs_, que o Cargo cria por padrão, vamos digitar o seguinte código: 17 | 18 | ```rust 19 | use std::io; 20 | 21 | fn main() { 22 | println!("Guess the number!"); 23 | 24 | println!("Please input your guess."); 25 | 26 | let mut guess = String::new(); 27 | 28 | io::stdin() 29 | .read_line(&mut guess) 30 | .expect("Failed to read line"); 31 | 32 | println!("You guessed: {guess}"); 33 | } 34 | ``` 35 | 36 | 37 | ### Vamos passar agora por cada linha do código 38 | 39 | Para conseguir o número que nosso usuário digitar precisamos da biblioteca ```io``` que vem da biblioteca ```standart```, conhecida como ```std```: 40 | 41 | ```rust 42 | use std::io; 43 | ``` 44 | 45 | Por padrão, o Rust tem um set de itens definidos na biblioteca standard, que traz isso para o escopo de todo programa. Esse set é chamado de _prelude_. 46 | 47 | Se você quiser algo que não está no _prelude_, precisa trazer isso para o escopo usando a declaração ```use```. Usar a biblioteca ```std:io``` traz várias funcionalidades úteis. Como a habilidade de aceitar entradas do usuário. 48 | 49 | Agora temos a função ``main`` que como vimos anteriormente, é a porta de entrada para todo programa em Rust. 50 | 51 | ```rust 52 | fn main() { 53 | ``` 54 | 55 | A sintaxe ```fn``` declara uma nova função. 56 | 57 | Depois usamos o macro ```println!``` para imprimir na tela a string. 58 | 59 | ```rust 60 | println!("Guess the number!"); 61 | 62 | println!("Please input your guess."); 63 | ``` 64 | 65 | 66 | #### Armazenando Valores com Variáveis. 67 | 68 | Agora criamos a variável que irá armazenar a entrada do usuário, assim: 69 | 70 | ```rust 71 | let mut guess = String::new(); 72 | ``` 73 | 74 | Por padrão, as variáveis no Rust são imutáveis, isso significa que uma vez que damos um valor para a variável, esse valor não irá mudar, e para criar uma variável mutável usamos a palavra ```mut``` antes do nome da variável: 75 | 76 | ```rust 77 | let bananas = 5; // imutavel 78 | let mut bananas = 5 // mutavel 79 | ``` 80 | 81 | Voltando ao nosso jogo de adivinhação, sabemos que ```let mut guess``` vai iniciar uma variável mutável chamada ```guess```. O símbolo de = diz ao Rust que queremos vincular algo a aquela variável, assim associamos aquela variável ao resultado de ```String::new```, uma função que retorna uma instancia de String, um tipo provido pela biblioteca standard. Que é um pedaço de texto codificado em UTF-8 dinâmico, que pode crescer. 82 | 83 | O ```::``` na linha ```::new``` indica que ```new```é uma função associada ao tipo String. Uma função associada é uma função que é implementada em um tipo, nesse caso String. A função ```new``` cria uma nova string vazia. 84 | 85 | 86 | #### Recebendo entrada do usuário 87 | 88 | Agora vamos usar o módulo a função ```stdin``` do módulo ```io``` que importamos lá no começo, ele nos permite lidar com a entrada do usuário. 89 | 90 | ```rust 91 | io::stdin() 92 | .read_line(&mut guess) 93 | ``` 94 | 95 | Agora a linha ```.read_line(&mut guess)``` chama o método ```read_line``` que pega a entrada do usuário e coloca isso em uma string, depois passamos ```&mut guess``` como argumento para dizer onde vamos armazenar a entrada do usuário. 96 | 97 | O ```&``` indica que esse argumento é uma referencia, que nos da um jeito acessar um dado na memória sem precisar copiar ele várias vezes. Também veremos mais a fundo depois. 98 | 99 | A função ```read_line``` também retorna um resultado, que é ```Ok``` ou ```Err``` e precisamos lidar com isso, esse resultado tem métodos definidos nele, como o método ```expect``` que é chamado caso a instancia do resultado seja um valor inválido (```Err```), ele vai quebrar o programa e mostrar a mensagem que você passou como argumento na tela, assim como fizemos no código 100 | 101 | ```rust 102 | .expect("Failed to read line"); 103 | ``` 104 | 105 | Caso retorne um ```Ok``` o _expect_ irá retornar, nesse caso, a quantidade de bytes na entrada do usuário. 106 | 107 | Se você não colocar o ```expect``` o programa vai compilar mas vai mostrar um aviso dizendo que o programa não vai lidar com um possível erro. 108 | 109 | #### Imprimindo Valores com println! 110 | 111 | Agora na próxima linha temos: 112 | 113 | ```rust 114 | println!("You guessed: {guess}"); 115 | ``` 116 | 117 | Essa linha imprime a string que contém a entrada do usuário, o { } podemos pensar como as garras de um caranguejo que seguram um valor no seu lugar. quando queremos imprimir o valor de uma variável podemos colocar ela dentro dos {} assim ```{guess}``` mas também podemos deixar {} vazios e depois passar as variáveis em ordem: 118 | 119 | ```rust 120 | let x = 5; 121 | let y = 10; 122 | 123 | println!("x = {x} and y + 2 = {}", y + 2); 124 | ``` 125 | 126 | Pronto! Agora você já tem a primeira parte do programa pronto e entende como ela funciona, se rodar usando ```cargo run``` você poderá digitar um número e ver ele impresso no seu terminal. 127 | 128 | #### Usando Crate 129 | 130 | Agora, para dar continuidade ao programa vamos ter que gerar o nosso número aleatório, para isso vamos usar uma _biblioteca crate_ (crate é o termo do Rust para um binário ou uma biblioteca), que contém o código que pode ser usado em outros programas. 131 | 132 | Para adicionar, vamos ao nosso arquivo _Cargo.toml_ e vamos escrever: 133 | 134 | ```toml 135 | [dependencies] 136 | rand = "0.8.5" 137 | ``` 138 | 139 | Assim dizemos ao Cargo quais dependências externas queremos usar no nosso projeto. 140 | 141 | Agora se você rodar ```cargo build``` vai ver uma diferença no que aparece no terminal, pois o Cargo vai baixar e compilar todas as dependências novas. 142 | 143 | Quando instalamos crates e especificamos a versão ela ficará salva no _Cargo.lock_ e mesmo que saiam novas versões das dependências elas não serão alteradas se você não alterar explicitamente no _Cargo.toml_ ou usando ```cargo update```. 144 | 145 | ```console 146 | $ cargo update 147 | Updating crates.io index 148 | Updating rand v0.8.5 -> v0.8.6 149 | 150 | ``` 151 | 152 | Dessa forma ele irá ignorar as versões 0.9.0, e se quiser vai ter que alterar no _Cargo.toml_: 153 | 154 | ```toml 155 | [dependencies] 156 | rand = "0.9.0" 157 | ``` 158 | 159 | E na próxima vez que você rodar ```cargo build``` o Cargo vai atualizar o registro das crates e reavaliar seus requisitos de acordo com a nova versão. 160 | 161 | ## Gerando Número Aleatório 162 | 163 | Agora vamos usar a crate _rand_ para gerar um número aleatório: 164 | 165 | ```rust 166 | use std::io; 167 | use rand::Rng; 168 | 169 | fn main() { 170 | 171 | println!("Guess the number!"); 172 | 173 | let secret_number = rand::thread_rng().gen_range(1..=100); 174 | 175 | println!("The secret number is: {secret_number}"); 176 | 177 | println!("Please input your guess:"); 178 | 179 | let mut guess = String::new(); 180 | 181 | io::stdin() 182 | .read_line(&mut guess) 183 | .expect("Failed to read line"); 184 | 185 | println!("You guessed: {guess}"); 186 | } 187 | ``` 188 | 189 | Assim geramos um número aleatório no range de 1 a 100 e armazenamos na variável imutável ```secret_number```, depois mostramos o resultado na tela. 190 | 191 | Vamos precisar comparar o valor de ```guess``` com o ```secret_number``` para saber se o jogador acertou ou errou, mas para isso primeiro temos que transformar o ```guess``` que inicialmente recebe uma string, em número, assim: 192 | 193 | ```rust 194 | let guess: u32 = guess.trim().parse().expect("Invalid number"); 195 | ``` 196 | 197 | Especificamos que ```guess``` será do tipo u32, vamos ver isso mais pra frente, por agora saiba que é um número positivo, e caso o valor de ```guess``` digitado seja um número válido vamos fazer o parse, caso contrário o programa finalizará com o erro especificado "Invalid number". 198 | 199 | Após essa verificação, para finalizar o jogo vamos usar as expressões condicionais if/else, comparando se ```guess``` é igual a ```secret_number```, se sim, vai imprimir na tela que o jogador ganhou, se não, vai ser impresso que o jogador perdeu. 200 | 201 | ```rust 202 | if guess == secret_number { 203 | println!("You win!"); 204 | } else { 205 | println!("You lost!"); 206 | } 207 | ``` 208 | 209 | Pronto, seu jogo de adivinhação feito em rust está finalizado, agora vamos rodar com ```cargo run```! 210 | 211 | Código final: 212 | 213 | ```rust 214 | use std::io; 215 | use rand::Rng; 216 | 217 | fn main() { 218 | println!("Guess the number!"); 219 | 220 | let secret_number = rand::thread_rng().gen_range(1..=100); 221 | 222 | println!("The secret number is: {secret_number}"); 223 | 224 | println!("Please input your guess:"); 225 | 226 | let mut guess = String::new(); 227 | 228 | io::stdin() 229 | .read_line(&mut guess) 230 | .expect("Failed to read line"); 231 | 232 | let guess: u32 = guess.trim().parse().expect("Invalid number"); 233 | 234 | println!("You guessed: {guess}"); 235 | 236 | if guess == secret_number { 237 | println!("You win!"); 238 | } else { 239 | println!("You lost!"); 240 | } 241 | 242 | } 243 | ``` 244 | 245 | -------------------------------------------------------------------------------- /05 - Entendendo Ownership/01 - O Que é Ownership.md: -------------------------------------------------------------------------------- 1 | # O Que é Ownership 2 | 3 | É uma das mais únicas ferramentas do Rust que permite deixar a memória do seu programa mais segura sem precisar de um garbage collector, como em algumas outras linguagens, é muito importante entender como isso funciona. 4 | 5 | Ownership é um set de regras que comandam em como um programa Rust controla a memória, todos os programas tem seus métodos para controlar o modo de como usam a memória enquanto estão rodando, algumas linguagens tem o garbage collector, que procura por memória que não está mais sendo usada e libera, em outras linguagens, o programador tem que alocar e liberar a memória manualmente. 6 | 7 | Em Rust a memória é gerenciada por um sistema de propriedade junto com um conjunto de regras do compilador, se alguma das regras forem violadas o programa não vai compilar. 8 | 9 | ## Regras do Ownership 10 | 11 | - Cada valor no Rust tem um dono. 12 | - Eles só podem ter um dono por vez. 13 | - Quando o dono sair do escopo, o valor será droppado (liberado, e deixará de existir). 14 | 15 | 16 | ## Escopo de Variável 17 | 18 | Vamos ver o escopo de algumas variáveis, o escopo é o lugar que a variável é válida 19 | 20 | ```rust 21 | { // s não é válida aqui, ainda não foi declarada 22 | let s = "hello"; // s é válida daqui em diante 23 | 24 | // faça algo com s 25 | } // o escopo acabou, a variável não é mais válida 26 | ``` 27 | 28 | Associamos a variável ```s``` a uma _string literal_ e podemos ver dois importantes pontos: 29 | - Quando ```s``` está no escopo, é valida. 30 | - Ela continua válida até sair do escopo. 31 | 32 | 33 | ## O Tipo String 34 | 35 | O tipo de string usado anteriormente é guardado na memória stack pois o compilador sabe o seu tamanho, o que precisamos agora é de um tipo mais complexo e dinâmico, que pode mudar e que seja armazenado na _heap_. 36 | 37 | O tipo ```String``` nos permite criar strings que não sabemos o valor na hora da compilação. 38 | 39 | ```rust 40 | let s = String::from("hello"); 41 | ``` 42 | 43 | Usamos o ```::``` para usar a função particular de ```String``` e criar uma string. 44 | 45 | ```rust 46 | let mut s = String::from("hello"); 47 | 48 | s.push_str(", world!"); // push_str() coloca uma string literal 49 | 50 | println!("{}", s); // hello, world! 51 | ``` 52 | 53 | Por que o tipo ```String``` pode ser mutável e o tipo _literal_ não? A diferença é como esses dois tipos lidam com a memória. 54 | 55 | ## Memória e Alocação 56 | 57 | No caso de uma _string literal_ nós sabemos o conteúdo na hora de compilar, então o texto vai direto para o executável final, mas só funciona para strings que sabemos o quanto vamos usar, e não para strings dinâmicas, pois não sabemos quando espaço precisamos armazenar na hora de compilar, e o tamanho também pode mudar enquanto o programa está rodando. 58 | 59 | Com o tipo ```String``` criamos uma variável mutável, que pode crescer e diminuir, e para isso alocamos um monte de memória heap: 60 | 61 | - A memória precisa ser requisitada no alocador de memória durante o tempo de execução. 62 | - Precisamos de um modo de retornar essa memória para o alocador quando terminamos de usar a ```String``` 63 | 64 | Quando usamos ```String::from``` pedimos a memória que precisamos para uma nova String. 65 | 66 | No Rust a memória é automaticamente retornada (deixa de existir), uma vez que a variável que a possui sai do escopo. 67 | 68 | ```rust 69 | { 70 | let s = String::from("hello"); // s é válido daqui em diante 71 | 72 | // faça algo com s 73 | } // o escopo acabou e s não é mais válido 74 | ``` 75 | 76 | Quando uma variável sai do seu escopo, o Rust chama uma função especial para nós, chamada ```drop```, ele chama essa função automaticamente no final das chaves ```{}```. 77 | 78 | ### Variáveis e Interação de Data com Move. 79 | 80 | Múltiplas variáveis podem interagir com o mesmo dado de formar diferentes: 81 | 82 | ```rust 83 | let x = 5; 84 | let y = x; 85 | ``` 86 | 87 | Nós podemos chutar que provavelmente estamos associando o valor 5 para a variável ```x``` e depois fazendo uma cópia do valor de x e associando em y, agora temos duas variáveis iguais a 5. E é isso que está acontecendo, pois os números inteiros são valores fixos e conhecidos na hora da compilação, esses dois valores 5 são colocados na _stack_. 88 | 89 | Vamos ver agora a versão em ```String```: 90 | 91 | ```rust 92 | let s1 = String::from("hello"); 93 | let s2 = s1; 94 | ``` 95 | 96 | Parece muito similar a outra associação, com números, podemos pensar que está fazendo a mesma coisa agora, criando uma cópia de s1 e associando ela a s2, mas não é exatamente assim que funciona. 97 | 98 | Quando fazemos isso o que acontece agora é que os dados da ```String``` são copiados, o que significa que copiamos o ponteiro, o tamanho e capacidade que está na stack. 99 | 100 | Como dissemos, quando uma variável sai do seu escopo o Rust automaticamente chama a função ```drop``` e libera a memória _heap_ para aquela variável, e como vimos ambas as variáveis tem um ponteiro apontando para o mesmo lugar. Esse é um problema, quando s1 ou s2 saem do escopo as duas vão tentar liberar a mesma memória podendo corromper a memória. 101 | 102 | Para manter a segurança da sua memória, depois que você associa a variável ```let s2 = s1``` a variável ```s1``` não é mais válida, então o Rust não tem que liberar nada quando ```s1``` sair do escopo: 103 | 104 | ```rust 105 | let s1 = String::from("hello"); 106 | let s2 = s1; 107 | 108 | println!("{}, world!", s1); 109 | ``` 110 | 111 | O código acima retornará um erro, porque ```s1``` foi movido e agora apenas ```s2``` é dona daqueles dados. 112 | 113 | Em escopos: 114 | 115 | ```rust 116 | let s1 = String::from("hello"); 117 | 118 | { 119 | let s2 = s1; // s2 agora é dono de s1 e s1 é droppado 120 | } 121 | 122 | println("{s1}"); // erro, porque s1 não existe mais 123 | ``` 124 | 125 | Se quisermos copiar a data _heap_ da ```String``` e não apenas a _stack_, podemos usar um método comum chamado ```clone```: 126 | 127 | ```rust 128 | let s1 = String::from("hello"); 129 | let s2 = s1.clone(); 130 | 131 | println!("s1 = {}, s2 = {}", s1, s2); 132 | ``` 133 | 134 | Isso funciona perfeitamente, pois a data _heap_ é copiada. Não apontam pra o mesmo local e não ocupam o mesmo espaço em memória, diferente de quando associamos a ```String s1``` a ```s2``` fazendo s2 ser proprietária dos dados. 135 | 136 | 137 | ### Stack-Only Data: Cópia 138 | 139 | Isso funciona e é válido: 140 | 141 | ```rust 142 | let x = 5; 143 | let y = x; 144 | 145 | println!("x = {}, y = {}", x, y); 146 | ``` 147 | 148 | O motivo de ```x``` continuar sendo válido mesmo sem chamar ```clone``` é que ```x``` é um inteiro no qual vamos ter o tamanho dele na hora de compilar, então ele será armazenado na _stack_, é bem rápido fazer essa cópia, isso significa que não há motivo de prevenir ```x``` de continuar sendo válido mesmo depois de associar a outra variável. 149 | 150 | Rust tem uma notação especial chamada ```Copy```, que podemos colocar em tipos que são armazenados na memória _stack_, as variáveis que usam isso não são movidas, são trivialmente copiadas, deixando elas continuarem válidas depois de ser associadas a outra variável. 151 | 152 | Alguns tipos que implementam ```Copy```: 153 | - Todos os inteiros, como u32. 154 | - Booleanos. 155 | - Floating, como f64. 156 | - Char. 157 | - Tuplas. Mas só quando tem o mesmo tipo em todos os elementos como (i32, i32, i32). 158 | 159 | ## Ownership e Funções 160 | 161 | Passar valores para uma função é similar a associar um valor para uma variável . Passar uma variável para uma função vai mover ou copiar. 162 | 163 | ```rust 164 | fn main() { 165 | let s = String::from("hello"); // s vem no escopo 166 | 167 | takes_ownership(s); // o valor de s é passado para a função 168 | // ... s deixa de ser válido aqui 169 | 170 | let x = 5; // x vem no escopo 171 | 172 | makes_copy(x); // x seria movido para a função, 173 | // mas i32 é um tipo que pode ser copiado 174 | // então continua válido 175 | } 176 | 177 | fn takes_ownership(some_string: String) { // some_string vem no escopo 178 | println!("{}", some_string); 179 | } // aqui o escopo acabou é o drop é chamado para limpar o some_string 180 | 181 | fn makes_copy(some_integer: i32) { // some_integer vem no escopo 182 | println!("{}", some_integer); 183 | } // aqui some_integer sai do escopo mas nada especial acontece 184 | ``` 185 | 186 | 187 | ### Retornar Valores e Escopo 188 | 189 | Retornar valores também transfere o proprietário, o dono de uma variável sempre segue o mesmo padrão, associar um valor para outra variável, move ele. Quando uma variável que possui seus dados na _heap_ sai do escopo, o valor vai ser limpo pela função ```drop``` a não ser que o proprietário dos dados seja movido para outra variável. 190 | 191 | Pegar um dono e retornar ele para cada função é tedioso, e se quisermos que uma função use um valor mas não seja o proprietário dele? É chato que tudo que passamos também precisa ser passado de volta se quisermos usar novamente, além de qualquer dados resultantes da função que também possamos querer retornar. 192 | 193 | ```rust 194 | fn main() { 195 | let s1 = String::from("hello"); 196 | 197 | let (s2, len) = calculate_length(s1); 198 | 199 | println!("The length of '{}' is {}.", s2, len); 200 | } 201 | 202 | fn calculate_length(s: String) -> (String, usize) { 203 | let length = s.len(); // len() retorna o tamanho da string. 204 | 205 | (s, length) 206 | } 207 | ``` 208 | 209 | Mas isso é muito trabalho para um conceito que deveria ser comum, mas felizmente, o Rust possui um recurso para usar um valor sem transferir propriedade, é chamado de referências (_references_). --------------------------------------------------------------------------------