<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > Porque você deveria aprender Rust< / title >
< meta name = "description" content = "Por que você deveria aprender Rust" >
< meta name = "author" content = "Julio Biason" >
< meta name = "apple-mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-status-bar-style" content = "black-translucent" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui" >
< link rel = "stylesheet" href = "reveal.js/css/reveal.css" >
< link rel = "stylesheet" href = "reveal.js/css/theme/night.css" id = "theme" >
<!-- Code syntax highlighting -->
< link rel = "stylesheet" href = "reveal.js/lib/css/zenburn.css" >
<!-- Printing and PDF exports -->
< script >
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
< / script >
<!-- [if lt IE 9]>
< script src = "lib/js/html5shiv.js" > < / script >
<![endif]-->
< style type = "text/css" media = "screen" >
.happy {
color: yellow;
}
.reveal section img {
border: none;
}
.reveal ul.empty {
list-style: none outside;
}
li {
display: block;
}
.cursor {
background-color: #666;
color: white;
}
img {
max-height: 90%;
}
td.seen {
font-style: italic;
font-weight: bold;
}
.semi-opaque {
background-color: rgba(0, 0, 0, 0.7);
}
< / style >
< / head >
< body >
< div class = "reveal" >
< div class = "slides" >
< section >
< section data-background = "_images/rust-ferris.png" data-header >
< h2 class = "semi-opaque" > Porque Você Deveria Aprender Rust< / h2 >
< / section >
< / section >
< section >
< section >
< img src = "_images/avatar-20170726.png" alt = "Me" style = "float:left;width:200px;" class = "no-border" >
< div >
< ul class = "empty" >
< li > Júlio Biason< / li >
< li > https://functional.cafe/@juliobiason< / li >
< li > julio.biason@pm.me< / li >
< li > < a href = "http://presentations.juliobiason.net" > http://presentations.juliobiason.net< / a > < / li >
< / ul >
< / div >
< / section >
< / section >
< section >
< section >
< p >
< a href = "https://insights.stackoverflow.com/survey/2019" >
A languagem mais amada segundo o StackOverflow
Survey 2019
< / a >
< p class = "fragment" > ... pelo 4⁰ ano seguido.< / p >
< aside class = "notes" >
O resultado do StackOverflow é sobre qual
linguagem os programadores realmente gostam de
programar (e quais eles tem pavor de usar).
Pessoalmente, depois de 30 anos programando,
quando começei a brincar com Rust, eu
finalmente me diverti enquanto programava.
< / aside >
< / p >
< / section >
< / section >
< section >
< section >
< h2 > "Low Level Language with High Level Abstractions"< / h2 >
< / section >
< section >
< p > Resultado final com performance semelhante ao C...< / p >
< img src = "_images/rust-energy.png" alt = "" >
< aside class = "notes" >
Num estudo sobre quais linguagens consomem mais
energia, Rust chegou bem próximo de C.
Parte do trabalho de otimização do Rust vem da LLVM
(parte do pacote do Clang), mas a árvore de
abstração ainda é gerada pela linguagem -- o que
significa que o compilador Rust consegue "ajudar" o
LLVM a otimizar o código.
< / aside >
< / section >
< section >
< p > ... mas com abstrações em algo nível< / p >
< ul >
< li > Strings sem tamanho fixo< / li >
< li > Listas< / li >
< li > Mapas< / li >
< / ul >
< aside class = "notes" >
Ao contrário de C, em que só se mexe com ponteiros
pra cima e pra baixo, Rust tem todas as abstrações
de alto nível que estamos acostumados com outras
linguagens.
< / aside >
< / section >
< / section >
< section >
< section >
< h2 > Imutabilidade por Default< / h2 >
< aside class = "notes" >
Por que "imutabilidade" seria algo importante?
Imutabilidade muda a forma como pensamos nos dados,
e evita que o estado fique mudando quando não
queremos.
< / aside >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
fn main() {
let a = 2;
a = 3;
println!("{}", a);
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs" data-trim >
3 | let a = 2;
| -
| |
| first assignment to `a`
| help: make this binding mutable: `mut a`
4 | a = 3;
| ^^^^^ cannot assign twice to immutable variable
< / code > < / pre >
< aside class = "notes" >
Se você tentar mudar um dado depois de criado, o
compilador Rust não vai deixar.
< / aside >
< / section >
< section data-transition = "fade" >
< pre > < code class = "hljs" data-trim data-line-numbers = "7" >
3 | let a = 2;
| -
| |
| first assignment to `a`
| help: make this binding mutable: `mut a`
4 | a = 3;
| ^^^^^ cannot assign twice to immutable variable
< / code > < / pre >
< / section >
< section data-transition = "fade" >
< pre > < code class = "hljs" data-trim data-line-numbers = "5" >
3 | let a = 2;
| -
| |
| first assignment to `a`
| help: make this binding mutable: `mut a`
4 | a = 3;
| ^^^^^ cannot assign twice to immutable variable
< / code > < / pre >
< / section >
< section >
< img class = "stretch" src = "_images/Sorry-bout-that.gif" alt = "" >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
fn main() {
let mut a = 2;
a = 3;
println!("{}", a);
}
< / code > < / pre >
< aside class = "notes" >
... a não ser que você transforme sua variável em mutável.
Ainda, Rust tem funcionalidades para facilitar
copiar dados de um lado para o outro (como o
"spread operator" em structs).
< / aside >
< / section >
< / section >
< section >
< section >
< h2 > Borrow Checker< / h2 >
< aside class = "notes" >
O "Borrow Checker" é uma das principais novidades
do Rust em comparação com outras linguagens.
Ele basicamente controla como as variáveis vão ser
alocadas, quando serão desalocadas, quem pode
acessar o conteúdo da mesma e assim por diante.
< / aside >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
a = String::from("hello");
< / code > < / pre >
< / section >
< section >
"Variável < code > a< / code > tem o valor < code > "hello"< / code > "
< aside class = "notes" >
Em todas as linguagens que eu usei, sempre que via
uma atribuição, eu pensava "a variável X tem o
valor Y" -- mesmo em C.
< / aside >
< / section >
< section >
< div >
"Posição de memória apontada por < code > a< / code > tem o valor < code > "hello"< / code > "
< / div >
< div class = "fragment" >
< pre > < code >
0x3f5cbf89 = "hello"
< / code > < / pre >
< / div >
< aside class = "notes" >
Nunca uma linguagem me fez "despensar" no nome da
variável pra pensar que ela representa, na verdade,
uma posição de memória.
< / aside >
< / section >
< section >
< img src = "_images/rust-memory.png" alt = "" class = "stretch" >
< aside class = "notes" >
É mais ou menos isso que Rust "pensa" internamente
quando vê uma variável: uma posição de memória, de
um tamanho já definido, de um tipo definido.
E essa posição de memória *pertence* apenas à
variável indicada.
< / aside >
< / section >
< section >
< div >
A language that doesn't affect the way you think
about programming, is not worth knowing.
< / div >
< div >
-- Alan Perlis, "ALGOL"
< / div >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
fn main() {
let a = String::from("hello");
let _b = a;
println!("{}", a)
}
< / code > < / pre >
< / section >
< section >
< pre > < code >
error[E0382]: borrow of moved value: `a`
--> src/main.rs:5:20
|
4 | let _b = a;
| - value moved here
5 | println!("{}", a)
| ^ value borrowed here after move
|
= note: move occurs because `a` has type
`std::string::String`, which does not
implement the `Copy` trait
< / code > < / pre >
< aside class = "notes" >
O borrow checked não deixa a variável "a" ser
utilizada: quando a atribuímos "_b" o valor de "a",
o que estamos fazendo é indicando que aquela
posição de memória agora é controlada por "_b" e
não mais por "a".
< / aside >
< / section >
< section >
< p > E se eu precisar acessar a variável em mais de um lugar?< / p >
< h3 class = "fragment" > References< / h3 >
< aside class = "notes" >
Assim como C++, Rust tem o conceito de "referências".
< / aside >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
fn main() {
let a = String::from("hello");
let _b = &a;
println!("{}", a)
}
< / code > < / pre >
< / section >
< section >
< img src = "_images/rust-reference.png" alt = "" class = "stretch" >
< aside class = "notes" >
Uma referência nada mais é que um ponteiro para um
"controlador" de uma região de memória.
< / aside >
< / section >
< section >
< h3 > Regras do Borrow Checker< / h3 >
< p class = "fragment" >
Uma região de memória tem apenas um dono.
< / p >
< p class = "fragment" >
Passar um valor (região de memória) de uma variável
para outra, troca o dono.
< / p >
< p class = "fragment" >
A região é desalocada quando o dono sair de escopo.
< / p >
< aside class = "notes" >
Uma coisa engraçada sobre "quando sair de escopo" é
que existe uma função semelhante ao "free()" do C,
chamada "drop". Essa função não tem nada no corpo,
e recebe um parâmetro (sem ser por referência), se
tornando a dona da memória; assim, como ela termina
exatamente naquele ponto, a região de memória é
liberada.
< / aside >
< / section >
< section >
< h3 > Regras do Borrow Checker< / h3 >
< p class = "fragment" >
Uma região de memória pode ter infinitas referências.
< / p >
< p class = "fragment" >
... desde que elas não durem mais do que o dono.
< / p >
< aside class = "notes" >
Não é possível ter uma função que cria uma variável
e retorna apenas uma referência para essa variável:
no momento que a função for encerrada, ela irá
levar todas as variáveis com ela e as referências
se tornaram inválidas.
< / aside >
< / section >
< section >
< h3 > Regras do Borrow Checker< / h3 >
< p class = "fragment" >
É possível ter uma referência mutável de uma região de memória.
< / p >
< p class = "fragment" >
... mas para haver uma referência mutável ela deve ser
a < strong > única< / strong > referência.
< / p >
< / section >
< section >
< img src = "_images/dunno.jpg" alt = "" class = "stretch" >
< aside class = "notes" >
E o que isso ajuda, no final das contas?
< / aside >
< / section >
< section >
< img class = "stretch" src = "_images/builds-character.png" alt = "" >
< / section >
< section data-transition = "fade" >
< pre > < code class = "hljs go" data-trim > presente := Presente { ... }
canal < - presente
< / code > < / pre >
< aside class = "notes" >
Num exemplo em Go, criamos uma estrutura e passamos
essa estrutura para outra thread através de um
canal.
< / aside >
< / section >
< section data-transition = "fade" >
< pre > < code class = "hljs go" data-trim > presente := Presente { ... }
canal < - presente
presente.abrir()< / code > < / pre >
< aside class = "notes" >
... e depois de passar o presente pra outra pessoa,
nós abrimos o presente.
Mas se estamos entregando um presente pra alguém,
como é que estamos abrindo o presente?
O borrow checker não permite esse tipo de coisa:
Ele irá barrar a função atual de continuar
utilizando a variável porque, afinal de contas,
agora a região de memória pertence à outra função
(uma função que está rodando em outra thread).
< / aside >
< / section >
< section >
< h2 > GC?< / h2 >
< p class = "fragment" > GC não é determinístico.< / p >
< small class = "fragment" >
(mas ainda tem um container que é um contador de refências)
< / small >
< aside class = "notes" >
Um problema com GC é que ele é não determinístico:
uma hora o teu código roda rápido e outro momento
ele fica lento, sem que tu consiga entender o
porque.
Sem GC, a execução do código torna-se determinística e
tu pode ter certeza da velocidade de execução.
< / aside >
< / section >
< section >
< a href = "https://swift.org/blog/swift-5-exclusivity/" > Swift 5 Exclusivity Enforcement< / a >
< aside class = "notes" >
A ideia do borrow checker é tão interessante que
até o Swift 5 agora tem o seu próprio borrow
checker (com outro nome, mas o princípio da coisa é
basicamente o mesmo, apesar de ser um pouco mais
leve no Swift).
< / aside >
< / section >
< / section >
< section >
< section >
< h2 > Hora da anedota!< / h2 >
< img class = "stretch" src = "_images/senta-que-la-vem-historia.gif" alt = "" >
< / section >
< section >
< p > localtime< / p >
< p class = "fragment" > SimpleDateFormatter< / p >
< aside class = "notes" >
A muito tempo atrás, eu estava ajudando uma colega
a resolver um problema com processamento de eventos
num projeto em C. Aparentemente, quando um evento
era processado, acontecia do tempo de processamento
ficar errado (algo como ficar negativo ou levar
menos de 10ms pra fazer uma query num banco
oracle). Quando perguntei como ela estava
calculando o tempo, ela me falou que estava usando
o "localtime". Foi quando me lembrei que
"localtime" não é thread-safe e, por isso, quando
uma thread passava pela chamada da função, o valor
era "resetado".
Outra situação aconteceu recentemente: Num projeto
Java, começou a acontecer de, em alguns casos, a
função que convertia strings para Date começou a
dar resultados completamente errados.
Estranhamente, eu lembrei da questão do localtime e
perguntei se o projeto usava threads: sim; fui
direto no DuckDuckGo e procurei por
"simpledateformatter thread safe" e o primeiro
resultado foi uma pergunta do StackOverflow: "Why
isn't SimpleDateFormatter thread-safe?"
< / aside >
< / section >
< section >
< h3 > Rust resolveria isso?< / h3 >
< h2 class = "fragment" > Não< / h2 >
< h4 class = "fragment" > ... na verdade, nem ia compilar.< / h4 >
< aside class = "notes" >
Uma questão importante para o Rust são "Zero Cost
Abstractions", segundo a definição do Bjarne
Stroustrup, criado do C++: para que algo seja
aceito no compilador, é preciso que o custo de não
usar algo não acarrete nada; ou seja, tornar uma
função thread-safe simplesmente inserindo um mutex,
não é zero cost porque, se tu não estiver usando
threads, não faz sentido o mutex.
< / aside >
< / section >
< / section >
< section >
< section >
< h2 > Tipos Algébricos< / h2 >
< / section >
< section >
< h3 > enum< / h3 >
< pre > < code class = "hljs rust" data-trim >
enum IpAddr {
V4,
V6
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
enum IpAddr {
V4(String),
V6(String),
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
let home = IpAddr::V4(String::from("127.0.0.1");
match home {
V4(address) => println!("IPv4 addr: {}", address),
V6(address) => println!("Ipv6 addr: {}", address),
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
enum Option< T> {
Some(T),
None
}
< / code > < / pre >
< / section >
< / section >
< section >
< section >
< h2 > Error Control< / h2 >
< / section >
< section >
< pre > < code class = "hljs python" data-trim >
try:
something()
except Exception:
pass
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs java" data-trim >
try {
something();
} catch (Exception ex) {
System.out.println(ex);
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs c" data-trim >
FILE* f = fopen("someting.txt", "wb");
fprintf(f, "Done!");
fclose(f);
< / code > < / pre >
< / section >
< section >
< div >
Onde o erro foi tratado nisso?
< / div >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
enum Result< T, E> {
Ok(T),
Err(E),
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
match File::create("something.txt") {
Ok(fp) => fp.write_all(b"Hello world"),
Err(err) => println!("Failure! {}", err),
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
match File::create("something.txt") {
Ok(fp) => match fp.write_all(b"Hello world") {
Ok(_) => (),
Err(err) => println!("Can't write! {}", err),
}
Err(err) => println!("Failure! {}", err),
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
let mut file = File::create("something.txt").unwrap();
file.write(b"Hello world").unwrap();
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
let mut file = File::create("something.txt")?;
file.write(b"Hello world")?;
OK(())
< / code > < / pre >
< / section >
< / section >
< section >
< section >
< h2 > Macros< / h2 >
< small class = "fragment" > ?< / small >
< / section >
< / section >
< section >
< h2 > Structs< / h2 >
< section >
< pre > < code class = "hljs rust" data-trim >
struct Present {
package_color: String,
content: String
}
< / code > < / pre >
< aside class = "notes" >
Structs em Rust são basicamente o mesmo que em C.
< / aside >
< / section >
< / section >
< section >
< section >
< h2 > Traits/Generics< / h2 >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
enum Result< T, E> {
Ok(T),
Err(E),
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
trait Summary {
fn summarize(& self) -> String;
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
struct Super {
phrase: String
}
impl Summary for Super {
fn summarize(& self) -> String {
self.phrase
.split_whitespace()
.map(|word| word.chars().nth(0).unwrap())
.collect();
}
}
< / code > < / pre >
< / section >
< section >
< pre > < code class = "hljs rust" data-trim >
fn get_summary(summarizable: T) -> String
where T: Summary
{
...
}
< / code > < / pre >
< / section >
< section >
< h2 > Structs genéricas< / h2 >
< pre > < code class = "hljs rust" data-trim >
struct Point< T> {
x: T,
y: T
}
< / code > < / pre >
< / section >
< / section >
< section >
< section >
< h2 > Tests< / h2 >
< pre > < code class = "hljs rust" data-trim >
#[cfg(test)]
mod tests {
#[test]
fn testing() {
}
}
< / code > < / pre >
< / section >
< section >
< pre > < code >
$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs
Running target/debug/deps/adder-ce99bcc2479f4607
running 1 test
test tests::testing ... ok
< / code > < / pre >
< / section >
< / section >
< section >
< section >
< h2 > Crazy stuff< / h2 >
< / section >
< section >
< a href = "https://medium.com/@shnatsel/how-rusts-standard-library-was-vulnerable-for-years-and-nobody-noticed-aebf0503c3d6" > How Rust’s standard library was vulnerable for years and nobody noticed< / a >
< / section >
< section >
< a href = "https://medium.com/@sgrif/no-the-problem-isnt-bad-coders-ed4347810270" > No, the problem isn’t “bad coders”< / a >
< / section >
< section >
< img src = "_images/rust-issues.png" alt = "4.5k issues no Github" class = "stretch" >
< / section >
< section >
< a href = "https://rustup.rs/" > rustup< / a >
< div class = "fragment" >
< small > stable-x86_64-pc-windows-msvc< / small >
< / div >
< div class = "fragment" >
< small > armv7-unknown-linux-gnueabihf< / small >
< / div >
< div class = "fragment" >
< small > wasm32-unknown-unknown< / small > < small class = "fragment" > (WebAssembly)< / small >
< / div >
< / section >
< / section >
< section >
< section >
< h2 > Falando em WASM...< / h2 >
< / section >
< section >
< h2 > WASM< / h2 >
< p > < a href = "https://rustwasm.github.io/wasm-pack/installer/" > wasm-pack< / a > < / p >
< / section >
< section >
< h2 > < a href = "https://wasi.dev/" > WASI< / a > < / h2 >
< p > The WebAssembly System Interface< / p >
< / section >
< / section >
< section >
< section >
< h2 > Bibliotecas< / h2 >
< / section >
< section >
< h3 > Rayon< / h3 >
< pre > < code >
fn sum_of_squares(input: & [i32]) -> i32 {
input.iter()
.map(|& i| i * i)
.sum()
}
< / code > < / pre >
< / section >
< section >
< h3 > Rayon< / h3 >
< pre > < code >
fn sum_of_squares(input: & [i32]) -> i32 {
input.par_iter()
.map(|& i| i * i)
.sum()
}
< / code > < / pre >
< / section >
< section >
< h3 > Log-Derive< / h3 >
< pre > < code >
#[logfn(ok = "TRACE", err = "ERROR")]
fn call_isan(num: & str) -> Result< Success, Error> {
if num.len() > = 10 & & num.len() < = 15 {
Ok(Success)
} else {
Err(Error)
}
}
< / code > < / pre >
< / section >
< / section >
< section data-background = '_images/thats-all-folks.jpg' >
< div class = "semi-opaque" >
< ul class = "empty" >
< li > Júlio Biason< / li >
< li > https://functional.cafe/@juliobiason< / li >
< li > julio.biason@pm.me< / li >
< li > < a href = "http://presentations.juliobiason.net" > http://presentations.juliobiason.net< / a > < / li >
< / ul >
< / div >
< / section >
< / div >
< / div >
< script src = "reveal.js/lib/js/head.min.js" > < / script >
< script src = "reveal.js/js/reveal.js" > < / script >
< script >
// Full list of configuration options available at:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
// showNotes: true,
transition: 'slide', // none/fade/slide/convex/concave/zoom
// Optional reveal.js plugins
dependencies: [
{ src: 'reveal.js/lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'reveal.js/plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'reveal.js/plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'reveal.js/plugin/zoom-js/zoom.js', async: true },
{ src: 'reveal.js/plugin/notes/notes.js', async: true }
]
});
< / script >
< / body >
< / html >