Folha de Referência Rápida do Rust
Referência rápida de sintaxe para variáveis, propriedade, empréstimo, structs, enums, traits, generics, lifetimes, iteradores e closures.
Variables & Mutability
let x = 5; // immutable (default)
let mut y = 5; // mutable
let z: i32 = 5; // explicit type
// Shadowing — rebind with new value/type
let x = x + 1; // shadow x (still immutable)
let x = x.to_string(); // shadow to change type
// Constants — compile-time, must have type
const MAX: u32 = 100_000;
// Destructuring
let (a, b, c) = (1, 2, 3);
let Point { x, y } = point;▶ Try itPrimitive Types
// Integers i8 i16 i32 i64 i128 isize // signed u8 u16 u32 u64 u128 usize // unsigned let n: i32 = 1_000_000; // underscores for readability let h = 0xff; let b = 0b1010; // hex, binary // Floats f32 f64 (default) let pi: f64 = 3.14159; // Boolean let t: bool = true; // Character (Unicode scalar, 4 bytes) let c: char = '🦀'; // Tuples let tup: (i32, f64, bool) = (1, 2.0, true); let first = tup.0; // Arrays — fixed length, same type let arr: [i32; 5] = [1, 2, 3, 4, 5]; let zeros = [0; 10]; // 10 zeros
Control Flow
// if / else if / else — also an expression
let x = if condition { 1 } else { 2 };
// loop — infinite, can return a value
let result = loop {
if done { break 42; }
};
// while
while n < 100 { n *= 2; }
// while let
while let Some(x) = stack.pop() { println!("{}", x); }
// for — iterating
for item in collection { ... }
for i in 0..10 { ... } // 0 to 9
for i in 0..=10 { ... } // 0 to 10
for (i, v) in v.iter().enumerate() { ... }
// match — exhaustive
match value {
1 => println!("one"),
2 | 3 => println!("two or three"),
4..=9 => println!("four to nine"),
x if x < 0 => println!("negative: {}", x),
_ => println!("other"),
}
// if let — match one pattern
if let Some(n) = opt { println!("{}", n); }
if let Ok(v) = result { println!("{}", v); }Functions
fn add(a: i32, b: i32) -> i32 {
a + b // last expression returned (no semicolon)
}
// Early return
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 { return None; }
Some(a / b)
}
// Generic function
fn largest<T: PartialOrd>(list: &[T]) -> &T { ... }
// Multiple return values via tuple
fn min_max(v: &[i32]) -> (i32, i32) { (v[0], v[v.len()-1]) }
// Diverging function (never returns)
fn crash(msg: &str) -> ! { panic!("{}", msg); }▶ Try itOwnership & Move
// Heap types MOVE on assignment
let s1 = String::from("hello");
let s2 = s1; // s1 MOVED — cannot use s1 anymore
// Clone for a deep copy
let s2 = s1.clone(); // both s1 and s2 valid
// Copy types are implicitly duplicated (stack types)
// i32, u32, f64, bool, char, tuples of Copy types
let x = 5;
let y = x; // x still valid — Copy
// Ownership through functions
fn takes(s: String) { ... } // s moved in, dropped on return
fn gives() -> String { String::from("hi") } // moves out▶ Try itBorrowing & References
// Immutable borrow — many allowed at once
let len = calculate_length(&s1);
fn calculate_length(s: &String) -> usize { s.len() }
// Mutable borrow — exactly one, no others active
fn change(s: &mut String) { s.push_str(" world"); }
change(&mut s);
// Rules:
// • Many & refs OR one &mut ref — never both
// • References must always be valid (no dangling refs)
// Prefer &str over &String in parameters:
fn greet(name: &str) { ... } // accepts &String AND &"literal"
greet("Alice");
greet(&owned_string);Slices
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..]; // "world"
let all = &s[..]; // whole string
// &str is a string slice
let literal: &str = "hello"; // points to read-only memory
// Array / Vec slices
let a = [1, 2, 3, 4, 5];
let slice: &[i32] = &a[1..3]; // [2, 3]▶ Try itStructs
struct Point { x: f64, y: f64 }
// Instantiate
let p = Point { x: 1.0, y: 2.0 };
println!("{}", p.x);
// Update syntax
let p2 = Point { x: 3.0, ..p };
// Tuple struct
struct Color(u8, u8, u8);
let red = Color(255, 0, 0);
// Unit struct (no fields)
struct Marker;
// Methods
impl Point {
// Associated function (no self) — like a static method
fn origin() -> Self { Point { x: 0.0, y: 0.0 } }
fn distance(&self, other: &Point) -> f64 { // immutable
((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
}
fn translate(&mut self, dx: f64, dy: f64) { // mutable
self.x += dx;
self.y += dy;
}
}Enums & Pattern Matching
enum Message {
Quit, // no data
Move { x: i32, y: i32 }, // named fields
Write(String), // one value
Color(u8, u8, u8), // tuple
}
match msg {
Message::Quit => println!("quit"),
Message::Move { x, y } => println!("{},{}", x, y),
Message::Write(s) => println!("{}", s),
Message::Color(r, g, b) => println!("rgb({},{},{})", r, g, b),
}
// Destructuring in let
let Message::Write(text) = msg else { return; }; // Rust 1.65+
// @ binding — capture and test
match n {
x @ 1..=10 => println!("small: {}", x),
_ => println!("big"),
}Option & Result
// Option<T> — Some(value) or None
let opt: Option<i32> = Some(42);
let none: Option<i32> = None;
opt.unwrap() // panics if None
opt.unwrap_or(0) // default value
opt.unwrap_or_else(|| 0) // lazy default
opt.map(|x| x * 2) // transform Some, pass None through
opt.and_then(|x| if x > 0 { Some(x) } else { None })
opt.filter(|&x| x > 0)
opt.is_some() opt.is_none()
// Result<T, E> — Ok(value) or Err(error)
let ok: Result<i32, &str> = Ok(42);
let err: Result<i32, &str> = Err("oops");
ok.unwrap()
ok.unwrap_or(0)
ok.map(|x| x * 2)
ok.map_err(|e| format!("Error: {}", e))
ok.is_ok() ok.is_err()
// Convert between them
opt.ok_or("missing") // Option -> Result
result.ok() // Result -> OptionError Handling
use std::num::ParseIntError;
// ? propagates errors up the call stack
fn parse(s: &str) -> Result<i32, ParseIntError> {
let n = s.trim().parse::<i32>()?; // returns Err if parse fails
Ok(n * 2)
}
// Box<dyn Error> for multiple error types
use std::error::Error;
fn run() -> Result<(), Box<dyn Error>> {
let n: i32 = "42".parse()?;
let f = std::fs::read_to_string("file.txt")?;
Ok(())
}
// Custom error type
#[derive(Debug)]
enum AppError { Parse(ParseIntError), NotFound }
impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { AppError::NotFound => write!(f, "not found"), ... }
}
}Traits
// Define a trait
trait Summary {
fn summarize(&self) -> String;
fn preview(&self) -> String { // default implementation
format!("{}...", &self.summarize()[..20])
}
}
// Implement a trait
impl Summary for Article {
fn summarize(&self) -> String {
format!("{} by {}", self.title, self.author)
}
}
// Trait bounds — static dispatch
fn notify(item: &impl Summary) { println!("{}", item.summarize()); }
fn notify<T: Summary>(item: &T) { ... } // equivalent
fn notify<T: Summary + Display>(item: &T) { ... } // multiple bounds
// where clause — cleaner for complex bounds
fn foo<T>(x: T) where T: Summary + Clone + std::fmt::Debug { ... }
// Trait objects — dynamic dispatch
fn notify(item: &dyn Summary) { ... }
let items: Vec<Box<dyn Summary>> = vec![Box::new(article)]Generics
// Generic function
fn largest<T: PartialOrd>(list: &[T]) -> &T { ... }
// Generic struct
struct Pair<T> { first: T, second: T }
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.first > self.second { println!("{}", self.first); }
}
}
// Generic enum (built-in examples)
enum Option<T> { Some(T), None }
enum Result<T, E> { Ok(T), Err(E) }
// Monomorphization: Rust generates a specific version for each
// concrete type at compile time — zero runtime overheadLifetimes
// Explicit lifetime when compiler can't infer
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Struct holding a reference
struct Excerpt<'a> {
text: &'a str,
}
// Lifetime elision rules (inferred automatically):
// 1. Each ¶m gets its own lifetime
// 2. If one &self/&mut self param, its lifetime = output lifetime
// 3. Otherwise you need explicit annotations
// 'static — lives for entire program
let s: &'static str = "I live forever";Closures
// Basic closure
let double = |x| x * 2;
let add = |x, y| x + y;
// With type annotations
let multiply = |x: i32, y: i32| -> i32 { x * y };
// Closures capture their environment:
let factor = 3;
let triple = |x| x * factor; // captures factor by reference
// move — take ownership of captured variables
let s = String::from("hi");
let greeting = move || println!("{}", s); // owns s
// Closure traits:
// Fn — borrows immutably (can call multiple times)
// FnMut — borrows mutably (can call multiple times)
// FnOnce — takes ownership (can only call once)
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }▶ Try itIterators
let v = vec![1, 2, 3, 4, 5, 6];
// Adapter methods (lazy — nothing runs until consumed)
v.iter() // &T
v.iter_mut() // &mut T
v.into_iter() // T (consumes vec)
// Common adapters
.map(|x| x * 2)
.filter(|&&x| x % 2 == 0) // note: &i32 with iter()
.filter_map(|x| if x > 0 { Some(x) } else { None })
.flat_map(|x| vec![x, x * 2])
.take(3) .skip(2)
.enumerate() // (index, value)
.zip(other_iter) // (a, b) pairs
.chain(other_iter)
// Consuming (terminal) methods
.collect::<Vec<_>>()
.sum::<i32>()
.product::<i32>()
.count()
.any(|&x| x > 3)
.all(|&x| x > 0)
.find(|&&x| x > 3) // Option<&T>
.position(|&x| x == 3) // Option<usize>
.max() .min()
.fold(0, |acc, &x| acc + x)
.for_each(|x| println!("{}", x))▶ Try itString Types
// &str — immutable borrowed slice (usually what you want in params)
let s: &str = "hello";
// String — owned, heap-allocated, growable
let mut s = String::new();
let s = String::from("hello");
let s = "hello".to_string();
let s = "hello".to_owned();
// Conversion
let owned: String = s.to_string(); // &str → String
let slice: &str = &owned; // String → &str (free)
let slice: &str = owned.as_str(); // explicit
// Building strings
s.push_str(" world");
s.push('!');
let combined = format!("{} {}", s1, s2); // allocates
let combined = [s1, s2].join(" ");
// Useful methods
s.len() s.is_empty()
s.contains("hi") s.starts_with("he") s.ends_with("lo")
s.to_uppercase() s.to_lowercase()
s.trim() s.trim_start() s.trim_end()
s.replace("old", "new")
s.split(' ') s.split_whitespace()
s.chars() s.bytes()
s.parse::<i32>().unwrap()Collections
// Vec<T>
let mut v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3];
v.push(4); v.pop(); // Option<T>
v.len() v.is_empty()
v[0] v.get(0) // panics vs Option<&T>
v.contains(&3)
v.iter() v.iter_mut() v.into_iter()
v.sort() v.sort_by(|a, b| a.cmp(b))
v.dedup() v.retain(|&x| x > 0)
Vec::with_capacity(n) // pre-allocate
// HashMap<K, V>
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("key", 42);
map.get("key") // Option<&V>
map.contains_key("key")
map.remove("key") // Option<V>
map.len()
// Insert-or-update idiom:
*map.entry("key").or_insert(0) += 1;
map.entry("key").or_insert_with(Vec::new).push(val);
// HashSet<T>
use std::collections::HashSet;
let mut set: HashSet<i32> = HashSet::new();
set.insert(1); set.contains(&1); set.remove(&1);
set.union(&other) set.intersection(&other)Modules & Visibility
mod utils {
pub fn helper() {} // public
fn internal() {} // private (module only)
pub(crate) fn crate_fn() {} // visible within crate
pub struct Config {
pub name: String, // field must also be pub
secret: String, // private field
}
}
// Bring into scope
use utils::helper;
use std::collections::{HashMap, HashSet};
use std::io::{self, BufRead}; // self = io module itself
use std::fmt::Display as Fmt; // rename with as
// Paths
utils::helper(); // absolute from crate root
super::parent_fn(); // parent module
self::local_fn(); // current moduleSmart Pointers
// Box<T> — heap allocation, single owner
let b = Box::new(5);
println!("{}", *b); // deref transparently
// For recursive types:
enum List { Cons(i32, Box<List>), Nil }
// Rc<T> — reference counted, single-threaded shared ownership
use std::rc::Rc;
let rc = Rc::new(5);
let rc2 = Rc::clone(&rc); // cheap clone, increments count
Rc::strong_count(&rc); // → 2
// Arc<T> — Rc but atomic (thread-safe)
use std::sync::Arc;
let arc = Arc::new(5);
// RefCell<T> — runtime borrow checking (interior mutability)
use std::cell::RefCell;
let cell = RefCell::new(5);
*cell.borrow_mut() += 1; // mutable borrow at runtime
// Common combo: Arc<Mutex<T>> for shared mutable state
use std::sync::{Arc, Mutex};
let data = Arc::new(Mutex::new(0));
*data.lock().unwrap() += 1;Concurrency
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
// Spawn a thread — move ownership in
let handle = thread::spawn(move || {
println!("in thread");
});
handle.join().unwrap();
// Channels — message passing
let (tx, rx) = mpsc::channel();
thread::spawn(move || { tx.send(42).unwrap(); });
println!("{}", rx.recv().unwrap());
// Shared state with Arc<Mutex<T>>
let counter = Arc::new(Mutex::new(0));
let c = Arc::clone(&counter);
thread::spawn(move || {
*c.lock().unwrap() += 1;
}).join().unwrap();
// Send + Sync traits (auto-implemented)
// Send: safe to transfer ownership to another thread
// Sync: safe to share reference across threadsAsync / Await
// Declare async function — returns a Future
async fn fetch() -> String {
// .await suspends until the future is ready
reqwest::get("https://api.example.com")
.await.unwrap()
.text()
.await.unwrap()
}
// Runtime needed to drive futures (Tokio is most common)
#[tokio::main]
async fn main() {
let data = fetch().await;
println!("{}", data);
}
// Run concurrently (not in parallel by default)
let (a, b) = tokio::join!(task_a(), task_b());
// Spawn a task (like a thread but async)
let handle = tokio::spawn(async { heavy_work().await });
handle.await.unwrap();Derive Macros
#[derive(
Debug, // {:?} and {:#?} formatting
Clone, // .clone()
Copy, // implicit copy on assignment (only for small types)
PartialEq, // == and !=
Eq, // full equality (requires PartialEq)
PartialOrd, // < > <= >=
Ord, // full ordering (requires PartialOrd + Eq)
Hash, // use as HashMap key
Default, // Type::default() — zeros/empty
)]
struct Point { x: f64, y: f64 }
// serde (separate crate) — JSON/YAML/etc serialization
#[derive(serde::Serialize, serde::Deserialize)]
struct Config { name: String, port: u16 }Common Traits
// Display — {} formatting (implement manually)
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
// From / Into — type conversion
impl From<i32> for MyType {
fn from(n: i32) -> Self { MyType(n) }
}
let x = MyType::from(5);
let x: MyType = 5.into(); // Into is auto-implemented from From
// Iterator — implement to make custom types iterable
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<u32> { ... }
}
// Deref — dereference behaviour (*val)
// Drop — custom cleanup on drop
// AsRef — cheap reference conversion🦀
Pronto para começar?
26 desafios interativos gratuitos. Sem instalação. Sem conta necessária para começar.