Primul program și macro-ul println!

Subiect din roadmap-ul Rust · parte din „Introducere în Rust”

În Rust, afișarea mesajelor în consolă este prima interacțiune concretă pe care o ai cu limbajul și fundamentul oricărui debug. Macro-ul println! nu este o funcție obișnuită, ci o construcție extensibilă a compilatorului care gestionează formatarea șirurilor de caractere și conversia tipurilor de date. În practică, vei folosi acest macro constant pentru a verifica starea aplicației, a loga erori sau a valida input-ul utilizatorului înainte de a procesa date complexe. Înțelegerea modului în care funcționează formatul {} și a diferenței dintre println! și print! este esențială pentru a scrie cod curat și eficient, evitând erorile de tipar sau problemele de performanță în aplicațiile production-ready.

Concepte cheie

  • Macro vs. Funcție: println! se termină cu !, indicând faptul că este un macro care generează cod la compilare, oferind flexibilitate în numărul de argumente.
  • Formatare prin poziție: Curburile goale {} sunt placeholder-uri care sunt umplute în ordinea argumentelor furnizate după șirul de format.
  • Trait-ul Display: Pentru a afișa un tip de date custom, acesta trebuie să implementeze trait-ul Display, care controlează reprezentarea textuală.
  • Trait-ul Debug: Dacă nu implementezi Display, poți folosi {:#?} pentru a imprima structura internă a obiectelor, utilă în dezvoltare.
  • Escape Characters: Secvențele precum \n (linie nouă) sau \t (tab) sunt interpretate automat pentru formatarea ieșirii.
  • Compile-time Checking: Erorile de formatare (de exemplu, lipsa unui argument) sunt detectate la compilare, nu la runtime, sporind siguranța codului.
  • Stdout vs Stderr: Deși println! scrie în stdout, în aplicații serioase se preferă separarea logurilor informative de cele de eroare.

Exemplu practic

Să presupunem că construiești o aplicație CLI pentru gestionarea task-urilor. Vrei să afișezi un rezumat al unui task creat recent.

fn main() {
    let task_name = String::from("Finalizare roadmap Rust");
    let priority = 9;
    let is_completed = false;

    // Folosirea directă a variabilelor
    println!("Task nou: {}", task_name);
    
    // Formatare combinată
    println!("Prioritate: {}, Completat: {}", priority, is_completed);
    
    // Utilizarea Debug pentru un tip complex (ex: Option)
    let deadline: Option<String> = None;
    println!("Termen limită: {:?}", deadline);
}

Aici observăm cum {} convertește automat valorile folosind Display, în timp ce {:?} apelează Debug. Acest lucru este crucial când lucrezi cu tipuri standard precum Vec<T> sau Option<T> unde nu ai control direct asupra implementării Display.

Greșeli frecvente

  • Confundarea print! cu println!: Uitați de \n la final poate lăsa cursorul pe aceeași linie, făcând output-ul greu de citit în terminal.
  • Ignorarea warning-urilor de formatare: Dacă treci mai multe argumente decât {}, compilatorul va genera un warning. Deși codul compilează, comportamentul poate fi imprevizibil sau ineficient.
  • Trecerea referinților incorecte: Macro-ul preia ownership sau clonează argumentele după nevoie, dar înțelegerea tipurilor (String vs &str) ajută la optimizare.
  • Abuzul Debug în producție: Folosirea {:#?} pentru a afișa date sensibile sau structuri mari în logs poate expune informații interne sau încetini aplicația.

Întrebări de interviu

  1. Care este diferența dintre println! și print!? println! adaugă automat un caracter de linie nouă (\n) la finalul șirului, mutând cursorul pe următoarea linie, în timp ce print! nu o face.

  2. De ce se termină println! cu semnul exclamării? Semnul ! indică faptul că este un macro, nu o funcție. Macros sunt procesate de compilator înainte de generarea codului binar, permițând verificarea tipurilor și a numărului de argumente static.

  3. Ce trait trebuie implementat pentru a folosi {} pe un tip custom? Trebuie implementat trait-ul std::fmt::Display. Dacă nu este implementat, nu poți folosi {}, dar poți folosi {:?} dacă ai implementat Debug.

Exercițiu

Scrie un program Rust care simulează un jurnal de activități simple. Creează trei variabile: user_name (String), action (String) și timestamp (un integer simplu care reprezintă ora). Folosește println! pentru a afișa o linie de log care să arate: [Ora] User 'nume' a efectuat acțiunea 'actiune'. Apoi, modifică programul pentru a afișa același mesaj, dar de data aceasta folosește macro-ul format! pentru a construi șirul într-o variabilă intermediară înainte de a-l printa. Timp estimat: 30-45 minute.

Resurse gratuite

Alte subiecte din „Introducere în Rust”