Introdução: por que Rust para API REST?
Rust é uma linguagem de programação de sistemas moderna que combina o desempenho do C++ com a segurança de memória. Nos últimos anos, tornou-se uma escolha popular para criar serviços web de alta carga. Se você já conhece Python e quer experimentar algo novo, Rust será um ótimo passo adiante.
Neste artigo, criaremos uma API REST completa para gerenciar uma lista de tarefas (Todo List) em Rust usando o framework Actix-web. Todo o processo levará no máximo 30 minutos se você seguir as instruções.
1. Preparação do ambiente
Instalação do Rust
Se você ainda não tem o Rust instalado, execute o comando no terminal (Linux/macOS):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shPara Windows, baixe o instalador do site oficial.
Após a instalação, verifique a versão:
rustc --versionCriação do projeto
Vamos criar um novo projeto com o Cargo, o gerenciador de pacotes do Rust:
cargo new rust_todo_apicd rust_todo_apiAdição de dependências
Abra o arquivo Cargo.toml e adicione as seguintes dependências:
[dependencies]actix-web = "4"actix-rt = "2"serde = { version = "1", features = ["derive"] }serde_json = "1"tokio = { version = "1", features = ["full"] }Salve o arquivo. O Cargo baixará automaticamente todas as bibliotecas na primeira compilação.
2. Criação do modelo de dados e armazenamento
Antes de escrever os endpoints, vamos definir a estrutura de dados. Usaremos um vetor simples em memória (para demonstração).
Modelo de tarefa
Crie o arquivo src/models.rs:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]pub struct Task { pub id: u32, pub title: String, pub completed: bool,}Armazenamento global
Em src/main.rs, adicionaremos o estado global. Para segurança de threads, usaremos Mutex:
use std::sync::Mutex;use actix_web::{web, App, HttpServer, HttpResponse, HttpRequest, Responder};use models::Task;
mod models;
struct AppState { tasks: Mutex<Vec<Task>>, next_id: Mutex<u32>,}3. Implementação dos endpoints CRUD
Agora vamos escrever os principais manipuladores. Todos eles receberão o estado da aplicação através de web::Data.
GET /tasks — obter todas as tarefas
async fn get_tasks(data: web::Data<AppState>) -> impl Responder { let tasks = data.tasks.lock().unwrap(); HttpResponse::Ok().json(tasks.clone())}POST /tasks — criar uma nova tarefa
async fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> impl Responder { let mut tasks = data.tasks.lock().unwrap(); let mut next_id = data.next_id.lock().unwrap(); let new_task = Task { id: *next_id, title: body.title.clone(), completed: false, }; tasks.push(new_task.clone()); *next_id += 1; HttpResponse::Created().json(new_task)}GET /tasks/{id} — obter tarefa por ID
async fn get_task(data: web::Data<AppState>, path: web::Path<u32>) -> impl Responder { let id = path.into_inner(); let tasks = data.tasks.lock().unwrap(); match tasks.iter().find(|t| t.id == id) { Some(task) => HttpResponse::Ok().json(task), None => HttpResponse::NotFound().json("Task not found"), }}DELETE /tasks/{id} — excluir tarefa
async fn delete_task(data: web::Data<AppState>, path: web::Path<u32>) -> impl Responder { let id = path.into_inner(); l