line!, file! și column! în Rust

Dau poziția din codul sursă unde sunt scrise: numărul liniei, coloana și numele fișierului. Utile în mesaje de jurnal și erori.

Sintaxă
line!()          // u32: linia pe care apare macro-ul
column!()        // u32: coloana
file!()          // &'static str: calea fișierului sursă, așa cum a primit-o compilatorul
module_path!()   // &'static str: modulul curent, de exemplu "main::plati"

Ce face line!, file! și column!

line!() se înlocuiește la compilare cu numărul liniei pe care este scris (un u32, numerotarea începe de la 1), column!() cu coloana, iar file!() cu calea fișierului sursă. module_path!() dă calea modulului (crate::modul). Așa funcționează mesajele lui panic! și dbg!, care arată fisier:linie:coloana.

Calea dată de file!() este exact cea primită de compilator: în proiectele Cargo este de forma src/main.rs, dar dacă fișierul a fost compilat cu o cale absolută, rezultatul este absolut. Dacă ai nevoie doar de numele fișierului, folosește Path::new(file!()).file_name().

Folosite într-un macro propriu, aceste macro-uri dau poziția apelului macro-ului, nu a definiției lui, deci poți scrie un macro de jurnal care arată de unde a fost apelat.

Exemple

Exemplul 1: Poziția în fișier

Fiecare line!() dă linia pe care este scris. Numele fișierului este scos din cale, pentru că aici calea completă este un director temporar.

use std::path::Path;

fn main() {
    let fisier = Path::new(file!()).file_name().unwrap().to_str().unwrap();
    println!("Fișier: {fisier}");
    println!("Sunt pe linia {}", line!());
    println!("Iar acum pe linia {}, coloana {}", line!(), column!());
    println!("Modul: {}", module_path!());
}

Rezultat

Fișier: main.rs
Sunt pe linia 6
Iar acum pe linia 7, coloana 59
Modul: main

Exemplul 2: Un macro de jurnal care arată locul apelului

line!() din macro dă linia unde a fost apelat log! (8 și 10), nu linia definiției macro-ului.

macro_rules! log {
    ($($arg:tt)*) => {
        println!("[linia {:>2}] {}", line!(), format!($($arg)*))
    };
}

fn main() {
    log!("pornesc aplicația");
    let utilizatori = 3;
    log!("{utilizatori} utilizatori conectați");
}

Rezultat

[linia  8] pornesc aplicația
[linia 10] 3 utilizatori conectați

Greșeli frecvente

  • Afișezi direct file!() și te miri că apare o cale lungă sau diferită pe alt calculator. Calea depinde de cum a fost apelat compilatorul; păstrează doar numele fișierului dacă asta te interesează.
  • Scrii o funcție fn log(msg: &str) care folosește line!(): va arăta mereu linia din funcția log. Pentru linia apelantului, folosește un macro sau atributul #[track_caller] cu std::panic::Location::caller().

Comenzi înrudite

  • dbg!: Afișează rapid, pentru depanare, fișierul, linia, expresia și valoarea ei (în format Debug) pe stderr, apoi returnează valoarea.
  • panic!: Oprește firul de execuție curent cu un mesaj de eroare. Se folosește pentru situații care nu ar trebui să apară niciodată (bug-uri).
  • concat!: Lipește literali (texte, numere, bool, char) într-un singur &’static str, la compilare, fără niciun cost la rulare.
  • macro_rules!: Definește un macro propriu: cod care scrie cod la compilare, după tipare. Așa sunt construite println!, vec! sau assert_eq!.