---
# System prepended metadata

title: Workshop de Rust

---

# Workshop de Rust

## Introdução

Leitura interessante:
https://heartbleed.com/

## Setup e impressões iniciais

Instalação: https://rustup.rs/

Só rodar o comando abaixo no shell:
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```

> OBS: O comando acima vai instalar a versão `stable` por padrão.


Para Windows (sem WSL), pode também optar por instalar pelo `chocolatey`. Ou em último caso em um `.exe` que pode ser baixado do site e instalado.


Para verificar a versão:
```
rustc --version
```

### Deu erro, e agora?
Se o erro foi:
```
note: please ensure that VS 2013, VS 2015, VS 2017 or VS 2019 was installed with the Visual C++ option
```
Instalar o Visual Studio Build Tools.

Se continuar:
>Se deu ruim pra alguem no windows, tenta colocar RUSTFLAGS na env do user, com o parametro "-C link-self-contained=yes"

---

Em distribuições baseadas em Debian, como Ubuntu, certificar que temos o `build-essentials` instalado:

```
sudo apt install build-essentials
```

### O que acabamos de instalar?
https://rust-lang.github.io/rustup/concepts/components.html

Instalado por padrão:
- `rustc` - Compilador
- `clippy` - Linter, é a ferramenta que avalia a qualidade do código e te entrega warnings que não impedem a compilação.
- `rust-docs` - Docs da lang
- `rust-std` - Biblioteca padrão
- `cargo` - Gerenciador de pacotes
- `rustfmt` - Formatador

Pode ser instalado depois, para oferecer suporte a editores de texto.
- `rls` - Rust Language Server


### Iniciando um projeto em Rust

1. Fazer um `cargo new meu-projeto`
2. `cd meu-projeto`
3. `cargo run` para compilar e rodar!


### Rodando no VSCode
Importante instalar essas extenões se quiser ter as features da linguagem no editor:
- https://marketplace.visualstudio.com/items?itemName=rust-lang.rust
- https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb

## Mão na massa
Nosso tour está disponível [aqui](https://cuchi.github.io/tour_of_rust/00_pt-br.html).

### Tipos primitivos:
```
    // Tipos primitivos
    // C               -> Rust
    // char            -> i8
    // short           -> i16
    // int             -> i32
    // unsigned char   -> u8
    // unsigned short  -> u16
    // unsigned int    -> u32
    // unsigned long   -> u64
    // float           -> f32
    // double          -> f64
    //                 -> bool
```

### O `println!`
É um macro, normalmente usamos assim:
```rust
println!("{:?}", valor); // Vai mostrar o valor "como ele é" na linguagem, útil para debugar.
println!("{}", valor); // Vai mostar o valor da maneira "bonita".
```

### Tipo `unit`, ou `()`
```
// Tipo "unit", equivalente ao "void" de C e C++
let x = ();
```

### Ponteiros
```rust
fn main() {
    let mut x = 42;
    muda_pra_10(&mut x);

    println!("{}", x);
}

fn muda_pra_10(x: &mut i32) {
    *x = 10
}
```

### Problemas
Esse daqui (usar o material até o capítulo 2):
https://projecteuler.net/problem=1

#### Resposta
```rust
// If we list all the natural numbers below 10 that are multiples of 3 or 5, 
// we get 3, 5, 6 and 9. The sum of these multiples is 23.
// Find the sum of all the multiples of 3 or 5 below 1000.

fn main() {
    println!("Soma imperativa {}", soma_imperativa());
    println!("Soma funcional {}", soma_funcional());
}

fn soma_imperativa() -> i32 {
    let mut sum = 0;

    for num in 1..1000 {
        if pode_somar(&num) {
            sum += num;
        }
    }

    return sum;
}

fn soma_funcional() -> i32 {
    (1..1000).filter(pode_somar).fold(0, |a, b| a + b)
}

fn pode_somar(num: &i32) -> bool {
    return num % 3 == 0 || num % 5 == 0;
}

```

### Bibliotecas em Rust
Podemos também criar bibliotecas usando o `cargo new <nome> --lib`, o Rust tem o https://crates.io/ como repositório de pacotes onde podemos publicar e usar bibliotecas nos nossos projetos.

### Projeto
Vamos fazer uma aplicação CLI.
- Vai usar código orientado a objetos
- Vai usar controle de erros
- Vai ler do stdin e usar argumentos de linha de comando

Vamos chamar ela de **tasks**:

```
./tasks new Estudar rust
./tasks list
./tasks done 3
./tasks delete 3
```

#### String primitiva vs não-primitiva
Temos 2 tipos de string:
- `String` - Struct que implementa uma String, na realidade é um vetor mutável por dentro (vamos ver isso depois ;). Preferimos usar essa quando estamos trabalhando com structs e código orientado a objetos.
- `&str` - String primitiva, não é mutável em nenhum caso

#### Traits
Caímos nesse problema:
```
`Task` doesn't implement `std::fmt::Display`
```
Ao fazer isso:
```rust
println!("{}", task);
```


Como resolvemos isso?
Investigando mais a fundo, vimos que `std::fmt::Display` é uma **Trait**:
https://doc.rust-lang.org/std/fmt/trait.Display.html

```rust
impl Display for Task {
    fn fmt(&self, formatter: &mut Formatter) -> Result {
        write!(formatter, "[{}]: done: {}", self.name, self.done)
    }
}
```

O código acima vai fazer com que a `task` seja exibida pelo `println!("{}", task)`


O mesmo de cima também é verdade para:
```rust
println!("{:?}", task);
```
Mas nesse caso, vai ser a trait de `Debug`. Nesse caso, conseguimos usar um macro:
```rust
#[derive(Debug)]
struct Task {
    name: String,
    done: bool,
}
```
Que vai gerar a implementação dessa **Trait** automaticamente.

#### Macro `vec!`
```rust
let tasks_vec = vec![
    Task{name: String::from("Estudar rust"), done: false},
    Task{name: String::from("Dormir"), done: true},
];

// Equivalente a:
// let mut tasks_vec = Vec::new();
// tasks_vec.push(Task{name: String::from("Estudar rust"), done: false});
// tasks_vec.push(Task{name: String::from("Dormir"), done: true});
```

#### Controle de erros
```rust
fn load(file_path: String) -> Result<TaskRepository, io::Error> {
    let result = fs::read_to_string(file_path)?;
    //                                        ^ Se falhar, vai retornar um
    //                                          Err(io:Error(...))
```


### Código do projeto

```rust
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::fs;
use std::io;

#[derive(Debug)]
struct CliError(String);

impl From<io::Error> for CliError {
    fn from(_: io::Error) -> CliError {
        CliError(String::from("Erro de I/O :("))
    }
}

#[derive(Debug)]
enum TaskStatus {
    Done,
    NotStarted,
    InProgress,
}

#[derive(Debug)]
struct Task {
    name: String,
    status: TaskStatus,
}

#[derive(Debug)]
struct TaskRepository {
    tasks: Vec<Task>,
}

impl Display for TaskStatus {
    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
        write!(
            formatter,
            "{}",
            match self {
                TaskStatus::Done => "tá pronto!",
                TaskStatus::InProgress => "tô fazendo",
                TaskStatus::NotStarted => "nem comecei ainda",
            }
        )
    }
}

impl Display for Task {
    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
        write!(formatter, "[{}]: status: {}", self.name, self.status)
    }
}

impl Task {
    fn parse_status(status: &str) -> Result<TaskStatus, CliError> {
        match status {
            "done" => Ok(TaskStatus::Done),
            "in_progress" => Ok(TaskStatus::InProgress),
            "not_started" => Ok(TaskStatus::NotStarted),
            _ => Err(CliError(String::from(format!(
                "status {} não existe!",
                status
            )))),
        }
    }
}

impl TaskRepository {
    fn load(file_path: String) -> Result<TaskRepository, CliError> {
        let result = fs::read_to_string(file_path)?;

        let mut tasks: Vec<Task> = Vec::new();
        let lines = result.split("\n");
        for line in lines {
            let columns: Vec<&str> = line.split(" | ").collect();
            if columns.len() != 2 {
                return Err(CliError(String::from(format!("Linha inválida:\n{}", line))));
            }

            tasks.push(Task {
                status: Task::parse_status(columns[0])?,
                name: String::from(columns[1]),
            })
        }

        Ok(TaskRepository { tasks: tasks })
    }

    fn save(&self, _file_path: String) {
        // Vamos fingir que ele salva em um arquivo ;)
    }

    fn add_task(&mut self, name: String) {
        self.tasks.push(Task {
            name: name,
            status: TaskStatus::NotStarted,
        })
    }

    fn length(&self) -> usize {
        self.tasks.len()
    }
}

fn main() -> Result<(), CliError> {
    let mut repository = TaskRepository::load(String::from("./tasks.txt"))?;
    println!("{:?}", repository);

    repository.add_task(String::from("Concluir esse projetinho :D"));

    // ...

    repository.save(String::from("./tasks.txt"));

    println!("{}", repository.length());
    Ok(())
}

```

### Próximos passos:
Terminar o projetinho.

Daqui em diante:
https://cuchi.github.io/tour_of_rust/chapter_5_pt-br.html

Fica a dica:
https://rocket.rs/
