Tutorial de Rust: seguretat de memòria i concurrència

Darrera actualització: 04/12/2025
Autor: Isaac
  • Rust garanteix seguretat de memòria en compilació mitjançant propietat, préstec i lifetimes, sense fer servir recol·lector d'escombraries.
  • El sistema de tipus i les regles d'aliasing permeten concurrència sense data races usant mutex, canals i punters intel·ligents.
  • Càrrec, crates.io i un ecosistema actiu simplifiquen la gestió de dependències, compilació, proves i desplegament.
  • Comprendre structs, enums, Option i Result és clau per manejar errors i modelar dades segures en aplicacions concurrents.

llenguatge de programació C vs Rust avantatges i desavantatges

Rust ha esdevingut un d'aquests llenguatges que tot desenvolupador de sistemes acaba escoltant una vegada i una altra: ràpid com C i C++, però amb una obsessió gairebé maniàtica per la seguretat de memòria i la concurrència ben feta. No és màrqueting buit: el seu disseny gira precisament al voltant que el compilador detecti en temps de compilació errors que en altres llenguatges només veus quan el sistema ja està en producció… o quan cau.

Si t'interessa entendre com Rust aconsegueix memòria segura sense recol·lector d'escombraries i concurrència sense por a carreres de dades, aquest tutorial és per a tu. Repassarem des dels fonaments del llenguatge i el seu ecosistema fins als conceptes clau de propietat, préstecs, tipus compostos, eines com Càrrec, i fins i tot farem una ullada a atòmics i bloqueig des d'un punt de vista més accessible per als que s'inicien en concurrència, tot això amb focus en seguretat i rendiment.

Tutorial de Rust: rendiment, seguretat de memòria i concurrència

Rust és un llenguatge de programació de propòsit general i multiparadigma, pensat tant per programació de sistemes de baix nivell com per a projectes d'alt nivell, des de sistemes operatius, motors de jocs i navegadors fins a serveis web dalt rendiment. Va sorgir originalment a Mozilla amb l'objectiu de millorar la seguretat del programari, especialment en components tan delicats com un motor de navegador.

El seu gran senyal didentitat és que garanteix seguretat de memòria en temps de compilació sense utilitzar un recol·lector d'escombraries. En lloc d'això, Rust utilitza un sistema de propietat (ownership) i un verificador de préstecs (borrow checker) que rastreja la vida de cada valor i de les seves referències. Així s'eviten problemes clàssics com punters penjants, desbordaments de memòria intermèdia o fuites de memòria sense necessitat de recompte de referències automàtic ni GC.

A més, Rust està dissenyat per facilitar la concurrència segura. El seu model de tipus i de propietat impedeix que hi hagi condicions de carrera (data races) entre fils, almenys mentre es romangui en codi segur (safe Rust). Això vol dir que moltes situacions perilloses es detecten en compilació, abans dexecutar una sola línia.

Per tot això, grans empreses com Dropbox, Microsoft, Amazon o Google han adoptat Rust en parts crítiques de la seva infraestructura. I no és casualitat que faci anys que encapçali les enquestes de Stack Overflow com un dels llenguatges “més estimats” pels desenvolupadors: combina rendiment estil C++ amb un conjunt d'eines modern (Càrrec, crates.io) i una comunitat molt activa, els anomenats Rustaceans.

Conceptes bàsics: llenguatge de programació, tipus i memòria

Abans d'entrar a les particularitats de seguretat de memòria i concurrència, cal aclarir alguns conceptes generals que apareixen tot el temps quan treballes amb Rust, especialment si veniu d'altres llenguatges o comenceu a programar.

Un llenguatge de programació és, al final, un conjunt de regles i estructures que et permet descriure algorismes i transformar-los en programes executables. Rust es compila a codi màquina nadiu mitjançant el seu compilador rustc, de manera que el rendiment que obtens sol estar a l'alçada de C i C++.

La gestió de memòria és el procés pel qual un programa reserva i allibera blocs de memòria mentre s'executa. Els errors en aquesta zona solen ser fatals: fuites de memòria (no alliberar el que ja no es fa servir), corrupció de dades per escriure fora de límits, o fer servir memòria després d'alliberar-la. Rust afronta això amb un sistema de tipus molt fort i regles formals de propietat, préstec i temps de vida (lifetimes).

A Rust apareixen també termes com tipus i punters intel·ligents. Un tipus descriu quina mena de dades emmagatzema una variable (sencers, flotants, cadenes, estructures, etc.) i com es poden manipular. Els punters intel·ligents (per exemple, Box, Rc y Arc) són estructures que encapsulen adreces de memòria i afegeixen lògica extra per gestionar recursos de forma segura, com ara comptatge de referències compartides o moviment de valors al heap.

En l'àmbit de la concurrència, conceptes com condicions de carrera, mutex i canals esdevenen imprescindibles: una condició de carrera apareix quan diversos fils accedeixen i modifiquen alhora un recurs compartit sense la deguda coordinació; un mutex (mutual exclusion) assegura que només un fil entra a la secció crítica alhora; i els canals permeten enviar missatges entre fils sense compartir memòria directament.

Per què aprendre Rust: seguretat de memòria i concurrència sense por

Rust s'ha guanyat la seva fama perquè ofereix tres pilars molt valuosos per a la programació moderna: rendiment, seguretat i eines actuals. Vegem perquè aquests punts són tan rellevants.

Pel que fa al rendiment, Rust compila directament a binaris nadius sense necessitat de màquina virtual ni intèrpret. El model de zero cost (zero-cost abstractions) cerca que les abstraccions d'alt nivell no afegeixin sobrecàrrega en temps d'execució. Per això, és ideal per a desenvolupament de sistemes, videojocs, components de navegador o microserveis de baixa latència.

La seguretat de memòria es basa en la seva sistema de propietat i préstec. No hi ha recol·lector d'escombraries, però el compilador sap exactament qui és el propietari de cada recurs, quan deixa de ser necessari i es pot alliberar. Així s'eviten fugues, punters penjants i gran part dels errors que tradicionalment han fet tan perillosa la programació a C i C++.

En el terreny de la concurrència, Rust persegueix el que se sol anomenar “concurrency without fear”: el propi sistema de tipus impedeix que hi hagi data races en codi segur. Si vols compartir dades mutables entre fils, hauràs d'usar primitives adequades com Mutex, RwLock o Arc, i el compilador sassegurarà que les regles daliasing i mutabilitat es respectin.

  Com desinfectar Windows amb Malwarebytes pas a pas

L'experiència de desenvolupament es completa amb eines modernes com Càrrega, el gestor de paquets i infraestructura de compilació integrat, i un ecosistema ampli de biblioteques (crates) que cobreixen des de xarxes asíncrones (Tòquio) fins a frameworks web (Actix, Rocket, Axum). Tot això recolzat per una comunitat oberta, prolífica i força pacient amb qui comença.

Instal·lació i eines essencials: rustup, rustc i Càrrec

Per escriure i executar els teus primers programes a Rust, el normal és començar instal·lant el toolchain oficial usant rovell (veure la introducció completa a Rust), un senzill instal·lador i gestor de versions que funciona als principals sistemes operatius.

Amb rovell pots instal·lar, actualitzar i canviar entre diferents versions de Rust (estable, beta, nightly) sense trencar res. Només cal anar a la pàgina oficial d'eines de Rust i seguir els passos indicats per al vostre sistema. Un cop instal·lat, tindràs disponible el compilador rustc, el gestor de projectes cargo i el mateix rustup en el teu terminal.

El compilador rustc és qui transforma el teu codi font en binaris executables o llibreries. Encara que podries invocar-ho directament amb ordres com a rustc main.rs, a la pràctica gairebé sempre treballaràs a través de Càrrec, que s'encarrega de trucar a rustc amb les opcions adequades.

L'eina central del flux de treball és Càrrega. Amb unes quantes ordres pots crear nous projectes, gestionar dependències, compilar, executar, provar i publicar paquets a crates.io. Algunes ordres bàsiques molt usades són cargo new, cargo build, cargo run, cargo test y cargo check, que revisa el codi sense produir lexecutable final, ideal per detectar errors ràpid.

Si vols trastejar sense instal·lar res, Rust Playground (l'executor online oficial) i plataformes com Replit permeten escriure i executar petites peces de codi des del navegador, perfecte per experimentar amb exemples de memòria i concurrència sense haver de muntar l'entorn complet.

El teu primer programa: Hola, Rust i flux bàsic

La clàssica presa de contacte en qualsevol llenguatge és el famós “Hola, món”. A Rust, un fitxer main.rs mínim podria contenir una cosa tan senzilla com una funció main que imprimeix una cadena en pantalla.

La paraula clau fn indica que estem definint una funció, i main és el punt dentrada del programa. Entre claus hi ha el bloc de codi de la funció. Per escriure a la consola, es fa servir la macro println!, que accepta un literal de cadena (o una plantilla amb marcadors) i l'envia a la sortida estàndard acabant amb salt de línia.

Si compiles directament amb rustc main.rs, obtindràs un binari executable (per exemple, main o main.exe segons el sistema). En executar-lo, veuràs el missatge a la terminal. Però la forma idiomàtica de treballar amb Rust és deixar que Cargo porti les regnes del projecte.

Amb cargo new nombre_proyecto es crea automàticament una estructura de carpetes amb un src/main.rs ja preparat amb un “Hola, món” i un arxiu Cargo.toml que conté metadades i futures dependències. A partir d'aquí, cargo run compila i executa el binari, i s'encarrega de recompilar només quan detecta canvis.

Aquesta forma de treballar no només és còmoda, sinó que t'habitua des del principi a fer servir l'ecosistema estàndard de Rust, cosa que resulta molt útil quan comences a afegir crates per a concurrència, xarxes, testing o allò que necessitis.

// Declarem la funció principal: punt d'entrada del programa fn main() { // Fem servir la macro println! per imprimir text a la consola println!("Hola, món!"); }

Variables, mutabilitat i tipus de dades bàsiques

A Rust, les variables es declaren amb la paraula clau let, i per defecte són immutables. És a dir, una vegada els assignes un valor, no pots modificar-lo a menys que ho declaris explícitament com a mutable amb mut.

La immutabilitat per defecte ajuda a evitar errors de lògica subtils, especialment en programes concurrents on diversos fils podrien voler canviar el mateix valor. Si necessites canviar-ho, escrius alguna cosa com let mut contador = 0;, ia partir d'aquí podràs reassignar nous valors a contador.

Rust també permet l'anomenat ombrejar: podeu declarar una nova variable amb el mateix nom dins del mateix àmbit, ocultant l'anterior. No és el mateix que mutar, perquè estàs creant un valor nou (que fins i tot pot ser d'un altre tipus). Per exemple, podeu passar d'una cadena a un enter usant el mateix nom, sempre que sigui una nova declaració amb let.

El sistema de tipus de Rust és estàtic, cosa que significa que el tipus de cada variable es coneix en compilació. No obstant això, la inferència de tipus és força potent: si escrius let x = 5;, el compilador assumeix que és un i32 llevat que li indiquis el contrari. Pots afegir anotacions com let x: i64 = 5; quan vulguis ser explícit.

Entre els tipus escalars disponibles hi ha els enters amb i sense signe (i8, u8, i32, etc.), els flotants (f32, f64), els booleans (bool) i els caràcters Unicode (char). Aquests tipus simples solen ser barats de copiar i molts implementen el trait Copy, el que significa que en assignar-los o passar-los a una funció es copien en lloc de moure's.

Cadenes a Rust: &str i String

El maneig de text a Rust sol desconcertar una mica al principi perquè distingeix clarament entre “slices” de cadena i cadenes pròpies. Les dues peces clau són &str y String.

Un &str és un slice de cadena immutable: una vista sobre una seqüència de bytes UTF-8 emmagatzemada en algun lloc. El cas típic són els literals com "Hola", que són de tipus &'static str (viuen tota la vida del programa i estan incrustats al binari). Els slices no tenen les dades, només hi apunten.

  Com personalitzar a fons la barra de tasques a Windows 11: Guia completa i trucs avançats

String, en canvi, és una cadena pròpia, mutable i allotjada al heap. Es pot redimensionar, concatenar, passar entre funcions movent-ne la propietat, etc. Se sol fer servir quan vols construir text dinàmic o emmagatzemar-lo a llarg termini dins d'estructures.

En molts escenaris transformaràs entre l'un i l'altre: per exemple, crearàs un String::from("hola") a partir d'un slice, o prendràs prestat un &str d'un String en passar referències a funcions que només cal llegir.

Aquesta separació entre dades posseïdes i prestades és clau per a la gestió de memòria i s'estén a la resta del llenguatge: col·leccions, estructures i enums segueixen les mateixes idees de qui té i qui només mira.

Funcions, flux de control i comentaris

Les funcions a Rust es defineixen amb fn i permeten organitzar el programa en unitats lògiques reutilitzables. Cada funció especifica el tipus dels seus paràmetres i el tipus de tornada després d'una fletxa ->. Si no torna res significatiu, s'assumeix el tipus unitari ().

Un detall important és que la darrera expressió d'una funció (o de qualsevol bloc) sense punt i coma es pren com a valor de tornada implícita. Pots fer servir return per a devolucions primerenques, però en codi idiomàtic moltes vegades simplement deixes l'expressió final sense ;.

El flux de control es maneja amb els clàssics if/else, bucles loop, while y for. A Rust, if és una expressió que torna un valor, de manera que pots utilitzar-lo directament en un let, sempre que les branques tornin el mateix tipus. Els bucles for normalment iteren sobre rangs o iteradors de col·leccions i són lopció recomanada en lloc díndexs manuals.

Per documentar el codi i fer la vida més fàcil a qui vingui després (inclòs tu mateix d'aquí a un mes), pots fer servir comentaris de línia amb // o de bloc amb /* ... */. A més, Rust ofereix comentaris de documentació amb /// que es converteixen en docs generades, encara que això ja encaixa més en projectes grans.

Propietat, préstec i lifetimes: la base de la seguretat de memòria

Aquí arribem al cor del model de memòria de Rust: el sistema de ownership (propietat), borrowing (préstec) i lifetimes (temps de vida). Aquestes regles són les que permeten garantir que les referències sempre són vàlides i que la memòria s'allibera de manera segura sense escombraries acumulades.

Les regles bàsiques de propietat són senzilles d'enunciar, encara que al principi costi interioritzar-les: cada valor té un únic propietari; només pot existir un propietari alhora; i quan el propietari surt del seu àmbit, el valor es destrueix i la memòria s'allibera. Això s'aplica, per exemple, a un String: en acabar el bloc on es va declarar, s'invoca automàticament el drop que allibera la memòria del heap.

Quan assigneu un valor propi a una altra variable o el passeu per valor a una funció, la propietat es mou. Això implica que la variable original deixa de ser vàlida després del moviment. Aquesta semàntica de moviment evita dobles alliberaments, perquè mai no hi ha dos amos intentant alliberar el mateix recurs.

Per permetre que diverses parts del programa accedeixin a un mateix valor sense canviar de propietari, Rust introdueix les referències i el préstec. En prendre prestat, crees una referència &T (immutable) o &mut T (mutable) al valor sense transferir la propietat. El préstec està limitat per les regles del verificador de préstecs, que comprova que les referències no sobrevisquin a les dades a què apunten i que no es barregin accessos mutables i compartits de forma perillosa.

Les regles del préstec es resumeixen així: en un moment donat, en pots tenir o bé múltiples referències immutables a un valor, o bé una sola referència mutable, però no totes dues alhora. Això elimina les condicions de carrera en memòria compartida: o hi ha molts lectors, o hi ha un escriptor aïllat, mai lectors i escriptor simultanis sobre la mateixa dada al mateix instant.

Tipus compostos: structs, enums i punters intel·ligents

Rust proporciona diverses formes d'agrupar dades relacionades amb estructures més riques, començant pels structs. Un struct us permet definir un tipus personalitzat amb camps amb nom, per exemple un usuari amb email, nom, estat d'activitat i comptador d'inici de sessió.

Per crear una instància d'un struct, emplenes tots els seus camps, i pots marcar la variable que el conté com a mutable per modificar els seus valors posteriorment. A més, hi ha la sintaxi d'actualització de structs, que et permet construir una nova instància reutilitzant alguns camps d'una altra ja existent mitjançant ..otro_struct.

Els enums són un altre pilar essencial: permeten definir un tipus que pot ser una de diverses variants possibles, cadascuna amb les seves pròpies dades associades o sense. Un exemple clàssic és un enum per a adreces IP, amb una variant V4 que emmagatzema quatre octets i una altra V6 que desa una cadena amb la notació IPv6.

La llibreria estàndard de Rust porta dos enums importantíssims: Option<T> y Result<T, E>. El primer representa la presència o absència dun valor (alguna cosa o res), i sutilitza per evitar punters nuls; el segon modela operacions que poden tornar un resultat correcte o un error, imposant que el maneig derrors sigui explícit i segur.

Per gestionar memòria dinàmica i compartir dades, Rust compta amb punters intel·ligents com a Box<T>, que mou un valor al heap i manté la propietat única; Rc<T>, un recompte de referències compartit per a entorns d'un sol fil; i Arc<T>, Similar a Rc però segur per a múltiples fils. Usar-los correctament és crucial quan es combina memòria dinàmica amb concurrència.

Càrrec i l'ecosistema de crates

Càrrec és la cola que manté unit l'ecosistema de Rust: gestiona la compilació, les dependències i el cicle de vida del projecte. Cada projecte té un fitxer Cargo.toml que actua com a manifest, declarant-ne el nom, la versió, l'edició del llenguatge i les dependències externes.

  Corregit: Error del controlador del bus PCI intern de Windows 10

la secció d'aquest fitxer us permet llistar crates de tercers amb les vostres versions. Quan executes cargo build o cargo run, Càrrec descarrega automàticament aquests crates des de crates.io, els compila i vincula el teu projecte amb ells. Així de senzill afegeixes, per exemple, generadors de números aleatoris, frameworks web o llibreries criptogràfiques.

Entre les ordres més habituals estan cargo new per iniciar projectes binaris o cargo new --lib per a biblioteques; cargo build per compilar en mode depuració; cargo build --release per obtenir una versió optimitzada orientada a producció; i cargo test per executar la bateria de proves.

cargo check mereix menció especial: compila el codi fins a un punt intermedi sense generar binari, cosa que fa que sigui molt ràpid per detectar errors de compilació. És perfecte per a iterar ràpid mentre el borrow checker et va assenyalant problemes amb propietats, referències i lifetimes.

Gràcies a aquest ecosistema, és habitual estructurar els teus projectes com a petits crates ben definits, compartint codi entre ells i reutilitzant solucions creades per la comunitat. Per a concurrència avançada, per exemple, comptaràs amb crates com Tòquio per a programació asíncrona o crossbeam per a estructures de dades concurrents d'alt rendiment.

Concurrència a Rust: fils, mutex, canals i atòmics

La concurrència és un dels motius pels quals Rust desperta tant interès: permet aprofitar els processadors multinucli sense caure en els errors típics de fils i memòria compartida. Si és la primera vegada que us acostes a aquests temes, convé distingir diversos conceptes.

La concurrència implica executar múltiples tasques que s'encavalquen en el temps, ja sigui en un o diversos nuclis. A Rust, pots crear fils (threads) del sistema per fer treball en paral·lel, i el llenguatge et guia perquè compartir dades entre ells sigui segur. Un error clàssic és la condició de carrera, on dos fils accedeixen i modifiquen una dada alhora i el resultat depèn de l'ordre d'execució, cosa molt difícil de depurar.

Per coordinar l'accés a dades compartides, Rust es recolza en primitives com les mutex, que garanteixen exclusió mútua: només un fil pot entrar a la secció crítica al mateix temps. En combinació amb Arc<T> per compartir propietat entre fils, és possible construir estructures de dades compartides que compleixin les regles de propietat i préstec.

Una altra forma habitual de comunicació entre fils, molt fomentada a Rust, és el pas de missatges usant canals. Un canal té un extrem emissor i un altre receptor; els fils es passen missatges (valors) a través d'ell, cosa que redueix l'ús de memòria compartida mutable i simplifica el raonament sobre l'estat del sistema.

Quan s'aprofundeix en concurrència de baix nivell, apareixen els tipus atòmics, que són variables l'accés de les quals es realitza mitjançant operacions indivisibles des del punt de vista dels fils. Això permet implementar comptadors compartits, flags d'estat, cues lock-free i més. Dominar atòmics requereix entendre models de memòria i ordres daccés, per la qual cosa molts desenvolupadors prefereixen començar per mutexs i canals abans de submergir-se en aquests detalls.

Primers passos i recursos per aprendre concurrència i atòmics

Si estàs arribant a la concurrència sense experiència prèvia, el més assenyat és construir una base sòlida de conceptes generals abans datacar eines avançades com els tipus atòmics de Rust. Llibres com “Programming Rust” ofereixen una introducció gradual, però és normal que obres centrades en atòmics i locks resultin denses de primeres.

Per anar més còmode, convé familiaritzar-se primer amb fils tradicionals, exclusió mútua i pas de missatges a Rust. Jugar amb exemples de std::thread, std::sync::Mutex, std::sync::Arc i canals de std::sync::mpsc ajuda a interioritzar com el compilador us guia i quins errors evita.

En paral·lel, és molt recomanable repassar recursos introductoris de concurrència en general, fins i tot encara que no estiguin centrats en Rust: entendre què són les condicions de carrera, què significa bloqueig, què implica la memòria compartida davant del pas de missatges i com es fan servir els locks. Un cop aquests conceptes et resultin naturals, els atòmics deixen de ser “màgia negra” i es converteixen en una eina més, però molt delicada.

Quan tornis a textos més avançats sobre atòmics i locks a Rust, et serà molt més senzill seguir el raonament si ja tens clar quin problema intenta resoldre cada construcció: des d'un senzill comptador segur entre fils fins a estructures lock-free que minimitzen la contenció.

Al final, Rust t'ofereix tant primitives d'alt nivell com a eines de molt baix nivell, i la clau és triar sempre el nivell d'abstracció més segur que resolgui el teu problema, recorrent a atòmics i codi unsafe només quan realment aporti valor i entenguis bé les seves implicacions.

Tot aquest ecosistema de tipus, propietat, préstec, crates, eines i primitives de concurrència es combina per oferir un llenguatge en què es pot escriure programari ràpid, robust i mantenible, reduint al mínim moltes classes derrors que històricament han plegat la programació de sistemes. A mesura que practiquis amb petits projectes, exercicis com Rustlings i documentació oficial, aquests conceptes passaran de semblar regles estrictes a convertir-se en un aliat que t'avisa abans que la decisió arribi a producció.

introducció al llenguatge Rust amb exemples-0
Article relacionat:
Introducció completa a Rust: Guia pràctica per a principiants amb exemples