# 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/