Receber por email
← Voltar para o catálogo

rust-dev

Um guia prático para o primeiro dia de desenvolvimento de aplicações Rust, abordando modelos mentais, decisões chave, idiomatismos, anti-padrões e crates essenciais. Ideal para iniciar novos projetos em Rust.

29estrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add tenequm/skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/rust-dev/svg)](https://www.skillteca.com.br/skills/rust-dev)

Skills relacionadas

Ver todas de Dados e Análise →

Alerta por categoria

Receba novas skills de Dados e Análise toda segunda

1 email curto, só com as skills novas de Dados e Análise. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Rust Development - Day 1

A practical foundation for writing Rust apps well from the first commit. Not a textbook. Focuses on the differences from other languages, the day-1 decisions that shape everything else, and the small set of crates that cover most real apps.

When to Use

  • Starting a new Rust project (CLI, service, library)
  • Coming to Rust from Python, JavaScript, Go, Java/C#, or C++
  • Choosing between owned/borrowed types, smart pointers, trait objects vs generics
  • Picking error handling strategy (anyhow vs thiserror)
  • Deciding which crates to reach for
  • Configuring a minimal but opinionated Cargo.toml, clippy, and rustfmt

Day-1 Setup

# 1. Install the toolchain (rustup is the toolchain manager)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 2. Confirm components (rustfmt and clippy ship with stable, rust-src enables IDE features)
rustup component add rustfmt clippy rust-src

# 3. Create a project
cargo new my-app          # binary (src/main.rs)
cargo new --lib my-lib    # library (src/lib.rs)

# 4. The dev loop (memorize these four)
cargo check     # fast type-check, no codegen
cargo run       # build and run (binary)
cargo test      # build and run tests (incl. doctests)
cargo clippy    # lint (run before pushing)
cargo fmt       # format

# 5. Manage dependencies without editing Cargo.toml by hand
cargo add tokio --features full
cargo remove tokio
cargo update                 # recompute Cargo.lock within existing semver ranges

cargo update only moves within the version ranges already in Cargo.toml. Crossing a major version (1.x to 2.0) needs a Cargo.toml edit or cargo add <crate>@2.

rust-analyzer is mandatory. It is the language server every editor uses (VS Code, Zed, Neovim, Helix, RustRover uses its own engine but is comparable). In VS Code, install the rust-analyzer extension and set rust-analyzer.check.command to "clippy" so you get lint feedback on save.

Want a file watcher later? cargo install bacon, then run bacon in your project. Not needed on day 1.

The Rust Mental Model in 5 Ideas

Rust trades two things you take for granted in most languages (a garbage collector and exceptions) for compile-time guarantees about memory, data races, and error handling. The shape of the language follows from that trade.

1. Ownership: every value has exactly one owner

Think of values like physical objects. A book, a file, a network connection. At any moment, one variable owns it. You can:

  • Move it: let b = a; hands ownership to b. a is gone.
  • Borrow it immutably: &a lets others look at it. Many readers allowed.
  • Borrow it mutably: &mut a lets one person modify it. Exclusive access.
  • Clone it: a.clone() makes a deep copy. Both keep their own.

When the owner goes out of scope, the value is dropped (memory freed, file closed, lock released). No GC, no manual free. This is RAII, enforced by the compiler.

2. Aliasing XOR mutability

At any moment, a piece of data has either:

  • one mutable reference (&mut T), or
  • any number of immutable references (&T),

never both. This single rule is what eliminates data races and most use-after-free bugs. The borrow checker enforces it. When it complains, it is telling you your data ownership story is unclear, not that the language is being difficult.

3. Errors are values, not exceptions

There is no try/catch. Functions that can fail return Result<T, E>. Functions that can return nothing useful return Option<T>. The compiler forces you to handle both. The ? operator propagates errors up the call stack with one character:

fn read_config() -> Result<Config, anyhow::Error> {
    let text = std::fs::read_to_string("config.toml")?;  // ? = early-return on Err
    let config = toml::from_str(&text)?;
    Ok(config)
}

There is no null. Option<T> is None or Some(value). The compiler will not let you forget the None case.

4. Traits are not Java interfaces

A trait defines behavior. Types impl traits. So far so familiar. The differences:

  • Static dispatch is the default. When you write fn f<T: Display>(x: T), the compiler generates a separate copy of f for each concrete T you call it with (monomorphization, like C++ templates). Zero runtime overhead.
  • Dynamic dispatch is opt-in via dyn Trait (typically Box<dyn Trait> or &dyn Trait). One vtable lookup per call.
  • No inheritance. Traits compose. If you find yourself reaching for Deref to "extend" a type, stop and use composition or an enum.
  • Orphan rule: you can impl YourTrait for SomeoneElsesType or impl SomeoneElsesTrait for YourType, but not both foreign. This keeps dependency resolution sane.

5. The borrow checker is a design oracle

The most common newcomer mistake is treating compiler errors as obstacles to silence. They are not. Almost every borrow-check error reveals a real issue with who owns what. When you get stuck, the question is rarely "how do I make this compile" and almost always "what is the actual ownership relationship I want here?" Read the error. The compiler is unusually informative.

The 3 Questions for Every Function Signature

Before writing a function, ask: does it need to own, read, or modify the input?

fn consume(s: String)        // owns:    function takes responsibility, caller loses it
fn read(s: &str)             // reads:   function looks at it, caller keeps it
fn modify(s: &mut String)    // mutates: function changes it in place

Defaults that work 90% of the time:

  • Function parameters: prefer &str over String, &[T] over Vec<T> (these are slices, accept both owned and borrowed callers).
  • Function returns: return owned types (String, Vec<T>). Returning references means lifetimes; avoid until you need them.
  • Struct fields: prefer owned types (String, Vec<T>). Storing &str in a struct is the single most common newcomer trap and it cascades lifetime annotations through every type that holds your struct.

Day-1 Decision Table

One-line answers to the choices that come up first.

| Decision | Default | When to pick the other | |---|---|---| | String vs &str (struct field) | String | Almost never &str until you have a real reason and understand lifetimes | | String vs &str (function param) | &str | Use String only if you must own/store it inside | | Vec<T> vs &[T] (param) | &[T] | Vec<T> only if you must own | | Box<T> vs Rc<T> vs Arc<T> | Box<T> (single owner, heap) | Arc<T> for shared ownership across threads. Avoid Rc<T> as default; use Arc<T> so you do not refactor when you go async | | RefCell<T> vs Mutex<T> | Mutex<T> (or RwLock<T>) | Same reason: works in async/threads, while RefCell does not | | Option<T> vs Result<T, E> | Option<T> for "no value", Result<T, E> for "failed for a reason" | If the absence carries meaning the caller should handle, Result | | dyn Trait vs impl Trait / <T: Trait> | Generic (<T: Trait> or impl Trait) - static dispatch | Box<dyn Trait> when you need a heterogeneous collection (Vec<Box<dyn Animal>>) | | Errors in app code | anyhow::Result<T> everywhere | - | | Errors in library code | thiserror-derived enum | Never Box<dyn Error> in public library APIs - forces callers to downcast | | &self vs &mut self vs self | &self for getters, &mut self for setters, self for builders/consuming ops | - | | Module layout | Inline modules until a file gets long, then split | One module = one file is a Java/C# instinct, not a Rust one |

Idioms to Internalize Early

These appear in nearly every Rust program. Learn them in week 1.

? for error propagation. Replaces nine lines of match with one character.

let body = reqwest::get(url).await?.text().await?;

Iterator chains over manual loops. Compile to the same machine code

Compartilhar X LinkedIn