Rust (linguagem de programação)
Rust é uma linguagem de programação multiparadigma compilada desenvolvida pela Mozilla.[10] É projetada para ser "segura, concorrente e prática", mas diferente de outras linguagens seguras, Rust não usa coletor de lixo.[11][12] Possui suporte nativo ao WebAssembly.[13][14]
Rust | |
---|---|
Paradigma | Multiparadigma: |
Surgido em | 2010 (13–14 anos) |
Última versão | 1.77.2 (9 de abril de 2024[1]) |
Versão em teste | 1.78.0 (21 de março de 2024 | )
Criado por | Graydon Hoare |
Estilo de tipagem |
|
Principais implementações | |
Influenciada por | |
Influenciou | |
Plataforma |
|
Sistema operacional |
|
Licença: | MIT ou Apache 2.0[9] |
Extensão do arquivo: |
|
Página oficial | www |
A linguagem apareceu como um projeto pessoal de Graydon Hoare, empregado da Mozilla. A organização começou a apoiar o projeto em 2009 e anunciou-o em 2010. No mesmo ano, os esforços mudaram do compilador original (escrito em OCaml) para um auto-hospedado feito em Rust. Conhecido por rustc, conseguiu compilar-se pela primeira vez em 2011 e utiliza o LLVM como back-end. Foi lançada pela primeira vez uma versão numerada pré-alfa em 2012. Rust 1.0, a primeira versão estável, foi lançada em 15 de maio de 2015.[15]
Foi considerada pelo público a linguagem "mais amada" por seis anos consecutivos, de acordo com pesquisas conduzidas pelo site Stack Overflow de 2016 a 2023,[16][17][18][19][20][21][22][23] e está entre as 25 linguagens mais populares, de acordo com pesquisas conduzidas pela RedMonk desde 2018.[24][25][26][27][28][29]
Design
Rust se baseia nos seguintes princípios: segurança sem coletor de lixo, concorrência sem disputa de dados e abstração sem overhead.[30] Estes princípios fazem com que Rust seja rápida para ser usada em aplicações de baixo nível como o motor de renderização Servo[31] e também prática para projetos de alto nível.
Em Rust não existem ponteiros nulos ou ponteiros soltos, impossibilitando falhas de segmentação. Rust gerencia memória e recursos automaticamente,[32] sem necessitar de um coletor de lixo. A linguagem impede condição de corridas entre threads pois não é possível que duas threads possam modificar um mesmo valor ao mesmo tempo. Para que uma referência possa ser compartilhada entre várias threads, ela deve ser somente leitura. Existem diversas técnicas seguras de comunicação entre threads.[33]
O princípio de abstração sem overhead vem do C++. Nas palavras de Bjarne Stroustrup: "Você não paga por aquilo que você não usa. E mais: aquilo que você usa, não conseguiria programar melhor à mão".[34] Rust permite um alto grau de abstração através do sistema de traits, que são interfaces que podem ser implementadas separadamente da declaração de um tipo.[35] Tipos genéricos são utilizados extensamente.
O projeto Rust usa o conceito de "canais de lançamento", semelhante ao Mozilla Firefox; são 3 canais: Nightly, Beta e Stable ("estável"). Diariamente é lançada uma nova versão Nightly, e a cada seis semanas a última versão desse canal é promovida para Beta, e só receberá atualizações para corrigir falhas sérias. Simultaneamente, a última versão Beta é promovida para Stable.[36][37][38]
Eventualmente a sintaxe da linguagem evolui, e novas palavras-chave podem ser adicionadas. Para evitar a quebra de compatibilidade com códigos antigos, novas edições são lançadas, que projetos antigos podem ativar opcionalmente;[39] a última edição foi a 2021.[40] Também é possível usar palavras-chave como identificadores, usando a seguinte sintaxe:[41]
// `match` é uma palavra-chavefn r#match(needle: &str, haystack: &str) -> bool { haystack.contains(needle)}
Enumerações e casamento de padrões
Rust possui enumerações de tipagem forte, e suas variantes podem carregar valores diversos. Casamento de padrões é muito importante em Rust, pois as enumerações são a base do tratamento de erros.[42] Exemplo de declaração de enumerações e o casamento de padrões:
enum Browser { Chrome, Firefox, Safari, Edge, Ie(u8),}let browser = Browser::Ie(11);match browser { Browser::Chrome | Browser::Edge => println!("Chromium"), Browser::Ie(version) => println!("Internet Explorer {version}"), _ => println!("Outro navegador"),}
O comando match
pode retornar valores e também pode ser usado com outros tipos.[42] Exemplo:
let age = 27u8;let category = match age { // `0..=4` é um padrão inclusivo (inclui 0, 1, 2, 3 e 4) 0..=4 => "bebê", 5..=13 => "criança", 14..=17 => "adolescente", 18.. => "adulto",};println!("Com {age} ano(s) você é considerado {category}.");
Padrões irrefutáveis (que não podem falhar), como tuplas e estruturas, podem ser desconstruídos de forma mais simples. Exemplo:[43]
fn print_addr(addr: (&str, &str)) { let (protocol, domain) = addr; // Tupla desconstruída em duas variáveis println!("Protocolo: {protocol:?}\nDomínio: {domain:?}");}// Forma alternativa: desconstrução de parâmetrofn print_addr((protocol, domain): (&str, &str)) { println!("Protocolo: {protocol:?}\nDomínio: {domain:?}");}
Inexistência de valor
Rust usa enumerações para representar a inexistência de um valor na forma de enum Option<T> { None, Some(T) }
.[42] Exemplo:
let website = Some("https://www.wikipedia.org"); // `website: Option<&str>`match website { None => println!("website não especificado"), Some(addr) => println!("website: {addr}"),}// Forma alternativa: if-letif let Some(addr) = website { // Pode usar `addr` apenas neste escopo println!("website: {addr}");}// Forma alternativa: let-elselet Some(addr) = website else { panic!("website não especificado"); // Um `return` ou `break` também é aceitável aqui};// Pode usar `addr` neste escopo...
Existem métodos na biblioteca padrão que simplificam esse tipo de tratamento. Alguns exemplos:[44]
let url1 = None::<&str>; // `url1: Option<&str>`let url2 = None::<String>;// Fornece um valor reservalet website = url1.unwrap_or("http://www.example.com");// Fornece o valor padrão do tipo (string vazia)let website = url1.unwrap_or_default();// Constrói um valor reserva através de uma clausuralet domain = "www.example.com";let website = url2.unwrap_or_else(|| format!("http://{domain}"));// Aborta (pânico)let website = url1.unwrap();// Aborta com uma explicaçãolet website = url1.expect("endereço inexistente");
Semelhante ao operador ?.
em outras linguagens, também existem métodos que permitem trabalhar com o possível valor de forma segura. Alguns exemplos:[44]
let website = Some("https://www.wikipedia.org");// Transforma o valor, se houver algumlet len = website.map(|d| d.len()); // `len: Option<usize>`// Trabalha com o valor, se houver algum. A clausura precisa retornar Optionlet domain = website.and_then(|addr| addr.strip_prefix("https://")); // `domain: Option<&str>`
Tratamento de erros
Rust não possui exceções como em C++. Ao invés disso, possui duas formas de representar erros; a primeira é a macro panic!
que indica defeitos de um programa, como divisões por zero, e causam o encerramento do programa.[45] Exemplo:
for i in -5..5 { println!("{}", 10 / i); // Pânico: divisão por zero}panic!("isto não deveria acontecer!"); // Alerta "pânico" manualmente
Em alguns casos óbvios de "pânico", como na divisão por zero, o compilador pode se recusar a compilar o código. Rust permite recuperar de alguns "pânicos" com a função especial std::panic::catch_unwind
, que não deve ser usada para emular o tratamento de exceções de C++.[46]
A segunda forma é criando uma instância de enum Result<T, E> { Ok(T), Err(E) }
, que pode ser retornada para indicar se uma operação foi um sucesso ou não, como a leitura de um arquivo no disco.[45] Exemplo de um erro personalizado:
use std::error::Error;use std::fmt;// Erros podem ser de qualquer tipo#[derive(Debug)]#[non_exhaustive]enum ValueError { Empty, TooSmall, TooLarge,}// Descrição do erroimpl fmt::Display for ValueError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use ValueError::*; match self { Empty => "vazio", TooSmall => "número muito pequeno", TooLarge => "número muito grande", } .fmt(f) }}// Métodos para extrair backtraces, entre outras coisasimpl Error for ValueError {}// Função que pode falharfn verify(value: Option<i64>) -> Result<i64, ValueError> { let Some(n) = value else { return Err(ValueError::Empty); }; match n { ..=3 => Err(ValueError::TooSmall), 21.. => Err(ValueError::TooLarge), _ => Ok(n), }}
E o tratamento do erro:
fn main() { let x = Some(3); let y = Some(4); match verify(x) { Ok(n) => println!("Número: {n}"), Err(err) => eprintln!("Erro: {err}"), } match verify(y) { Ok(n) => println!("Número: {n}"), Err(err) => eprintln!("Erro: {err}"), }}
É possível simplificar o código retornando prematuramente os erros com o operador ?
:[45]
use std::error::Error;// `Box<dyn Error>` representa qualquer tipo que implemente `Error`type AnyError = Box<dyn Error>;fn main() -> Result<(), AnyError> { let x = Some(3); let y = Some(4); println!("Número: {n}", n = verify(x)?); println!("Número: {n}", n = verify(y)?); // A segunda chamada só ocorrerá se a primeira não falhar Ok(()) // Retorna "sucesso"}
Genéricos e ownership
Rust possui suporte a programação genérica[47] e o conceito de ownership[48] (posse). Valores podem ter apenas um dono; quando o dono sai de escopo, o valor é destruído. Valores podem ser emprestados (borrowing) ou movidos (move).[49] Em certas ocasiões, como em referências circulares, é necessário especificar o lifetime (tempo de vida) de uma referência.[50] Exemplo de uma árvore binária em Rust:
// `'a` é um lifetime, e `T` é um tipo genérico#[derive(Debug)]enum Tree<'a, T> { Empty, Node(T, &'a Tree<'a, T>, &'a Tree<'a, T>), // As referências poderiam ser `Box<Tree<T>>`}use Tree::{Empty, Node};let tree = &Node(5.96, &Empty, &Node(1.0, &Empty, &Empty));// O primeiro membro é movido; ignora demais membrosif let Node(value, ..) = tree { println!("{value:?}");}// Ignora o primeiro membro; o terceiro membro é emprestadoif let Node(_, left, ref right) = tree { println!("-> {left:?}"); println!("-> {right:?}");}
Rust é mais restrita que outras linguagens; um valor imutável pode ter várias referências, mas um valor mutável pode ter apenas uma. Para o segundo caso, é necessário usar uma das várias estruturas de referências como std::rc::Rc
, std::rc::Weak
, std::cell::Cell
(mutabilidade interna), std::cell::RefCell
, entre outras.[51] Outra possibilidade é o uso de alocação de arena.[52]
Se houver ambiguidade na invocação de um método genérico, o tipo pode ser explicitado com o símbolo turbofish (::<>
).[53] Exemplo:
let dollar = "5.96" .parse::<f64>() .expect("falha ao converter para `f64`");println!("US$ 1.00 = R$ {dollar}");
Também é possível passar valores constantes como parâmetros genéricos. Exemplo:[54]
struct Array<T, const N: usize> { data: [T; N],}let arr = Array { data: [0u64; 5] }; // `arr: Array<u64, 5>`
Strings
Rust possui dois tipos principais de strings: &str
e String
. Ambos são sempre UTF-8 válido.[55] Existe um tempo de vida especial chamado 'static
, que indica que a referência é válida até o fim da execução do programa. Todas os literais de strings possuem ele implicitamente:[50]
let hello: &str = "Olá, Mundo!"; // Implicitamente estáticalet hello: &'static str = "Olá, Mundo!"; // O mesmo que acima
Se uma string possui aspas ou barra inversa, é possível usar outros delimitadores para simplificar:[53]
let json = r#"{ "message": "Olá, Mundo\n" }"#; // Exemplo de JSONlet json = "{ \"message\": \"Olá, Mundo\\n\" }"; // O mesmo que acima
Enquanto &str
é estático e imutável, String
é alocado dinamicamente.[56] Exemplo:
use std::io;let mut nums = vec![];let mut buf = String::new(); // Novo buffer// Usa o buffer para ler a entrada padrão.// `Ok(n)` é a quantidade de bytes lidos, onde 0 equivale a EOF.while let Ok(1..) = io::stdin().read_line(&mut buf) { // `trim()` retorna uma referência (`&str`) sem os espaços em branco let Ok(num) = buf.trim().parse::<f64>() else { break }; nums.push(num); buf.clear(); // Limpa o buffer para reutilizar}println!("Entrada: {nums:?}");
Uma função pode usar std::borrow::Cow
para retornar &str
ou String
conforme a necessidade de mutação/ownership.[57] Além de &str
e String
, Rust possui vários outros tipos de strings especializados, como &std::ffi::CStr
e std::ffi::CString
(para compatibilidade com a linguagem C):[58]
use std::ffi::CStr;let hello: &CStr = c"Olá, Mundo!"; // Terminado com NUL (b'\x00')
Em situações onde bytes ASCII são mais adequados, o tipo &[u8]
pode ser usado:[53]
let hello: &[u8] = b"Ol\xC3\xA1, Mundo!"; // 'á' não é um caractere válido em ASCIIlet hello: &[u8] = &[79, 108, 195, 161, 44, 32, 77, 117, 110, 100, 111, 33]; // O mesmo que acima
Traços e orientação a objetos
Rust possui suporte a traços que definem o comportamento de um objeto, e podem ser implementados por estruturas, enumerações, uniões e tipos básicos.[59][60] Traços podem ser deriváveis, como o Debug
, onde é gerada uma implementação de forma automática usando o atributo #[derive(…)]
.[61] A implementação padrão é definida através de macros procedurais.[62] Exemplo de traços:
trait Animal { // Método estático que retorna uma string estática fn type_name() -> &'static str; // `&self` é uma referência a instância fn flee(&self); // O mesmo que `fn flee(&self) -> ();`}trait Feline: Animal { // Traços podem fornecer definições padrões para métodos fn meow(&self) { println!("Miau!"); }}
#[derive(Debug)] // Implementa `Debug`struct Cat<'a> { name: &'a str,}// Implementa `Animal`impl Animal for Cat<'_> { fn type_name() -> &'static str { "Cat" } fn flee(&self) { println!("{} fugiu.", self.name); }}impl Feline for Cat<'_> {}// E o mesmo para `Dog`…
let type_name = Cat::type_name(); // Método estáticolet cat = Cat { name: "Charlotte" }; // Nova instânciacat.flee(); // O mesmo que `Animal::flee(&cat);`cat.meow(); // O mesmo que `Cat::meow(&cat);`
Estruturas, enumerações, uniões e funções podem especificar os traços dos membros/parâmetros de forma estática (genérica):[59]
fn do_flee<T>(animal: &T)where T: Animal,{ animal.flee();}
// Ou o equivalente:fn do_walk<T: Animal>(animal: &T) { animal.flee();}
// Ou com um tipo anônimo (incompatível com o turbofish):fn do_walk(animal: &impl Animal) { animal.flee();}
Ou de forma dinâmica (trait objects):[60]
fn do_walk_dyn(animal: Box<dyn Animal>) { animal.flee();}
Independente do tipo concreto, a instância é passada da mesma maneira:[59][60]
let cat = Cat { name: "Charlotte" };let dog = Dog { name: "Duke" };do_walk(&cat);do_walk(&dog);do_walk_dyn(Box::new(cat));do_walk_dyn(Box::new(dog));
Por fim, traços podem ter "tipos associados" que devem ser especificados ao implementar.[63] Exemplo:
trait Iterator { type Item; /* ... */}impl Iterator for SequenceIter { type Item = u32; /* ... */}fn analyze(iter: &mut impl Iterator<Item = u32>) { /* ... */}
Iteradores e clausuras
Em Rust, todos os iteradores implementam trait Iterator { type Item; /* ... */ }
, que fornece métodos adaptadores para consumir ou criar novos iteradores. Também possui um literal para representar intervalos (um tipo de iterador).[64] No exemplo a seguir o programa lista os números primos entre 4 e 20:
let mut numbers = vec![];// `4..=20` é um iterador inclusivo (inclui 20)for i in 4..=20 { // `2..i` é um iterador exclusivo, ex.: `2..5` inclui 2, 3 e 4. // `|x| i % x != 0` é uma clausura que recebe `x` e retorna booliano. if (2..i).all(|x| i % x != 0) { numbers.push(i); }}println!("Os números primos entre 4 e 20 são: {numbers:?}");
Clausuras podem capturar valores do ambiente de três maneiras: emprestando imutavelmente, mutavelmente ou movendo. A última é realizada com a palavra-chave move
.[65] Exemplo:
use std::thread;let msg = "Olá, Mundo!".to_owned();thread::spawn(move || { println!("{msg}"); // `msg` movido aqui});
As clausuras são representadas por três traços:[65]
trait Fn<Args> { type Output; /* ... */ }
– empresta valores do ambiente imutavelmentetrait FnMut<Args>: Fn<Args> { /* ... */ }
– empresta valores do ambiente mutavelmentetrait FnOnce<Args>: FnMut<Args> { /* ... */ }
– move os valores do ambiente e pode ser chamada apenas uma vez
Exemplo:
fn convert<F>(num: f64, handler: F) -> f64where F: FnOnce(f64) -> f64, // Assinatura da clausura{ handler(num)}fn main() { let y = convert(499.0, |num| num * 5.96); println!("{y}");}
Clausuras são anônimas e não têm um tipo concreto previamente conhecido, mesmo que possuam a mesma assinatura; por esse motivo, para retornar uma clausura de uma função, é necessário usar uma sintaxe especial:[59]
fn convert() -> impl FnOnce(f64) -> f64 { move |num| num * 5.96}fn main() { let f = convert(); println!("{y}", y = f(499.99));}
Por fim, é possível passar estruturas tuplas ou variantes de enumerações onde se espera clausuras. Exemplo:
let seq: Box<_> = (1..=5).map(Some).collect();println!("{seq:?}"); // [Some(1), Some(2), Some(3), Some(4), Some(5)]
Unsafe
Certas atividades consideradas inseguras, como deferência de ponteiros e uso de funções unsafe
, precisam estar dentro de blocos unsafe
.[66] Dentro desses blocos é responsabilidade do programador evitar comportamento indefinido.[67] Exemplo do uso de Assembly inline:[68]
use std::arch::asm;// Multiplica `x` por 6 usando `shifts` e `adds`let mut x: u64 = 4;unsafe { asm!( "mov {tmp}, {x}", "shl {tmp}, 1", "shl {x}, 2", "add {x}, {tmp}", x = inout(reg) x, tmp = out(reg) _, );}assert_eq!(x, 4 * 6);
Ponteiros
A criação de ponteiros não é uma atividade insegura, mas a deferência é. Exemplo retirado da documentação oficial:[66]
let mut num = 5;let r1 = &num as *const i32; // Ponteiro constante para `num`let r2 = &mut num as *mut i32; // Ponteiro mutável para `num`unsafe { println!("r1 is: {}", *r1); // Deferência println!("r2 is: {}", *r2); // Deferência}
Unions
Rust suporta unions para compatibilidade com a linguagem C, e são inerentemente inseguros,[66] podendo resultar em comportamento indefinido se usados incorretamente.[69] Exemplo retirado da documentação oficial:[69]
#[repr(C)]union MyUnion { f1: u32, f2: f32,}let u = MyUnion { f1: 1 };let f = unsafe { u.f1 }; // Leitura de campo
Exemplos
Programa Olá Mundo
fn main() { println!("Olá, Mundo!");}
Pode ser compilado e executado com o seguinte comando:[70]
$ cargo run
Algoritmo de Trabb Pardo-Knuth
use std::{io, iter::zip};fn f(t: f64) -> f64 { t.abs().sqrt() + 5.0 * t.powi(3)}fn main() { let mut a = [0f64; 11]; for (t, input) in zip(&mut a, io::stdin().lines()) { *t = input.unwrap().parse().unwrap(); } a.iter().enumerate().rev().for_each(|(i, &t)| match f(t) { y if y > 400.0 => println!("{i} TOO LARGE"), y => println!("{i} {y}"), });}
Rust lida com transbordamento numérico retornando f64::NAN
.
Analisador sintático
Exemplo de um analisador sintático usando um parser combinator:
use nom::bytes::complete::{tag, take_while_m_n};use nom::{combinator::map_res, sequence::tuple, IResult};#[derive(Debug)]pub struct Color { pub red: u8, pub green: u8, pub blue: u8,}fn hex_primary(input: &str) -> IResult<&str, u8> { map_res( take_while_m_n(2, 2, |c: char| c.is_ascii_hexdigit()), |input| u8::from_str_radix(input, 16), )(input)}fn hex_color(input: &str) -> IResult<&str, Color> { let (input, _) = tag("#")(input)?; let (input, (red, green, blue)) = tuple((hex_primary, hex_primary, hex_primary))(input)?; Ok((input, Color { red, green, blue }))}fn main() { let (_, color) = hex_color("#2F14DF").expect("sintaxe inválida"); println!("#2F14DF => {color:?}");}
E as dependências no arquivo Cargo.toml
:
[dependencies]nom = "7.1"
Servidor HTTP
Exemplo de um web service RESTful (HTTP) usando funções assíncronas e serialização; responde com um JSON ao acessar http://localhost:3000/hello/Mundo
:
use axum::{extract::Path, response::Redirect, routing::get, Json, Router};use serde::Serialize;use time::{serde::rfc3339, OffsetDateTime};use tokio::net::TcpListener;#[derive(Serialize)]struct HelloResponse { message: String, #[serde(with = "rfc3339")] timestamp: OffsetDateTime,}async fn hello(Path(name): Path<String>) -> Json<HelloResponse> { Json(HelloResponse { message: format!("Olá, {name}!"), timestamp: OffsetDateTime::now_utc(), })}#[tokio::main]async fn main() { tracing_subscriber::fmt::init(); let app = Router::new() .route("/", get(|| async { Redirect::to("/hello/Mundo") })) .route("/hello/:name", get(hello)); let listener = TcpListener::bind(("127.0.0.1", 3000)).await.unwrap(); tracing::info!("listening on http://{}", listener.local_addr().unwrap()); axum::serve(listener, app).await.unwrap();}
E as dependências no arquivo Cargo.toml
:
[dependencies]axum = "0.7.1"tracing = "0.1.40"tracing-subscriber = "0.3.18"[dependencies.serde]version = "1.0"features = ["derive"][dependencies.time]version = "0.3.30"features = ["serde-well-known"][dependencies.tokio]version = "1.34"features = ["full"]
Interface de linha de comandos
Exemplo de uma implementação do echo do Unix:
use clap::Parser;/// Ecoa o(s) TEXTO(s) para a saída padrão.#[derive(Parser, Debug)]#[command(author, version)]struct Args { /// Não emitir o caractere de nova linha do final. #[arg(short = 'n')] strip_trailing_newline: bool, #[arg(value_name = "TEXTO")] strings: Vec<String>,}fn main() { let args = Args::parse(); let output = args.strings.join(" "); print!("{output}"); if !args.strip_trailing_newline { println!(); }}
E as dependências no arquivo Cargo.toml
:
[dependencies.clap]version = "4.4"features = ["derive"]
Interface gráfica
Exemplo de uma interface gráfica usando a plataforma Web:
use leptos::*;use std::num::Saturating;#[component]fn Counter(start: u8) -> impl IntoView { let count = RwSignal::new(Saturating(start)); let label = move || match count.get().0 { 0 => "Fim.".into(), 1 => "Encerra no próximo clique!".into(), n => format!("Encerra em {n} cliques."), }; let disabled = move || count.get().0 == 0; let decrement = move |_| count.update(|c| *c -= 1); view! { <div class="flex flex-col items-center justify-center h-screen"> <p class="font-sans text-lg">{label}</p> <button disabled={disabled} on:click=decrement>Contar</button> </div> }}fn main() { mount_to_body(|| view! { <Counter start=5 /> })}
E a folha de estilos (neste exemplo, é usado o framework Tailwind CSS):
@tailwind base;@tailwind components;@tailwind utilities;
E as dependências no arquivo Cargo.toml
:
[dependencies.leptos]version = "0.6.11"features = ["csr"]
O alvo wasm32 precisa ser instalado para compilação cruzada, além de um bundler (Trunk):
$ rustup target add wasm32-unknown-unknown$ cargo install --locked trunk
O Trunk espera por um arquivo index.html
na raiz do projeto:
<!DOCTYPE html><html lang="pt-BR"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link data-trunk rel="tailwind-css" href="/assets/css/index.css" /> <title>Contador</title> </head> <body></body></html>
E a configuração do Tailwind CSS para processar os arquivos:
/** @type {import('tailwindcss').Config} */module.exports = { content: ["./src/**/*.{html,js,rs}"], theme: { extend: {}, }, plugins: [], corePlugins: { preflight: false, },}
Após isso, o código pode ser compilado e servido no navegador com o seguinte comando:
$ trunk serve --open
Ferramentas
Cargo é a ferramenta de produtividade oficial, e usa arquivos TOML para listar dependências e configurações de um projeto. Um projeto pode ser testado usando o comando $ cargo test
[71][72] e formatado com $ cargo fmt
.[73] A documentação de um projeto pode ser gerada a partir de comentários especiais em Markdown, usando o comando $ cargo doc
.[72][74] Um conjunto de lints opcionais estão disponíveis, e o código pode ser checado usando o comando $ cargo clippy
.[73]
Rust possui uma implementação do Language Server Protocol, o rust-analyzer, que fornece autocompletar, formatação e refatoração independente do editor de texto ou ambiente de desenvolvimento integrado. Os seguintes editores dão suporte ao rust-analyzer de forma nativa ou através de extensões/plugins:[73][75]
- Visual Studio Code – fornecido pela extensão oficial
- Helix – suporte nativo
- Kate – suporte nativo
- GNOME Builder – suporte nativo
Outras ferramentas dão suporte a Rust através de implementações próprias sem o uso do rust-analyzer:
- RustRover – versão do IntelliJ IDEA para Rust
- Vim – fornecido pelo plugin oficial rust.vim
- Emacs – fornecido pelo plugin oficial rust-mode
- Sublime Text – fornecido pelo pacote oficial Rust Enhanced
- Eclipse – fornecido pelo plugin Corrosion
Software desenvolvido em Rust
- Renderização
- Mozilla Firefox[76]
- Gecko – motor de renderização do Firefox
- Servo – motor de renderização experimental
- Chromium[77]
- librsvg – um motor de renderização de SVG;[78][79] é usado pelo GNOME e pelo MediaWiki[80]
- Prime Video – um serviço de vídeo sob demanda; usa Rust nos apps através do WebAssembly[81]
- Rede
- cURL – oferece o hyper como opção de back-end HTTP, desenvolvido em Rust[82]
- Discord – um serviço de chat para gamers; usa Rust no servidor e no aplicativo[83]
- Fractal – um mensageiro baseado no protocolo Matrix[84]
- Tor – uma rede anônima; usa Rust em alguns componentes,[85] e uma nova versão está sendo desenvolvida em Rust do zero[86]
- OpenDNS – um serviço de DNS[87][88][89]
- Pingora – o novo proxy do Cloudflare, substituindo o nginx[90]
- Cloudflare Workers – Plataforma serverless com WebAssembly[91]
- Azure IoT Edge – um serviço de inteligência para IoT; o daemon de segurança usa Rust[92]
- npm Registry – um serviço de hospedagem de dependências para JavaScript e Node.js[93]
- Magic Pocket – sistema de armazenamento do Dropbox[94][95]
- OneSignal – um serviço de notificações push[96][97]
- Figma – um editor de desenho vetorial para prototipagem de interfaces gráficas; usa Rust no servidor multiplayer[98]
- Sistemas operacionais e componentes
- Android e Linux – suporte experimental para desenvolvimento de drivers e módulos secundários[99][100][101][102][103]
- Google Fuchsia – usa Rust em alguns componentes[104]
- Microsoft Windows – usa Rust em alguns componentes[105]
- Redox – um sistema operacional tipo Unix com micronúcleo[106]
- GNOME – usa Rust e alguns componentes do Servo no processamento de CSS[107]
- GStreamer – um framework multimídia; usa Rust em alguns plugins[108][109]
- Stratis – um sistema de arquivos usado pelo Fedora e RHEL[110]
- Outros
- 1Password – um gerenciador de senhas[111][112]
- ripgrep – um substituto moderno do grep; é usado pelo Visual Studio Code[113]
- Deno – um substituto do Node.js desenvolvido pelo criador original[114]
- SWC – um compilador e bundler de JavaScript e TypeScript, usado pelo Next.js,[115] Deno, Rollup, entre outras ferramentas[116]
- MeiliSearch – um motor de busca com interface HTTP[117]
- Blackbird – o motor de busca especializado em código fonte do GitHub[118]
- Helix – um editor de texto modal inspirado no Neovim e Kakoune[119]
- Xi – um editor de texto usado pelo Google Fuchsia[120][121]
Ver também
Referências
Bibliografia
- Klabnik, Steve; Nichols, Carol (6 agosto de 2019) [julho de 2018]. The Rust Programming Language. Covers Rust 2018 (em inglês) 2 ed. [S.l.]: No Starch Press. 526 páginas. ISBN 978-1-7185-0044-0
- Balbaert, Ivo (7 de novembro de 2017) [27 de maio de 2015]. Rust Essentials. A Quick Guide to Writing Fast, Safe, and Concurrent Systems and Applications (em inglês) 2 ed. [S.l.]: Packt Publishing. 264 páginas. ISBN 978-1-7883-9913-5
- Bos, Mara (janeiro de 2023). Rust Atomics and Locks. Low-Level Concurrency in Practice (em inglês). [S.l.]: O'Reilly Media, Inc. 250 páginas. ISBN 978-1-0981-1944-7
Ligações externas
- Sítio oficial
- Rust no GitHub
- «The Rust Programming Language» (em inglês). Livro oficial
- «Rust Playground» (em inglês). Experimente Rust online