Introducere în limbajul Rust

Laurențiu Nicola

Limbajul Rust

“Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.”
“A language empowering everyone to build reliable and efficient software.”

Rust

  • Proiect personal al lui Graydon Hoare (2006)
  • Adoptat de Mozilla (2009)
  • Self-hosted din 2011
  • Garbage-collected până în 2013
  • Versiunea 1.0 (stabilă) a apărut în mai 2015
  • O versiune nouă la fiecare 6 săptămâni
  • Ieri: Rust 1.91 (octombrie 2025)
  • Cel mai iubit limbaj (Stack Overflow, 2016–2025)

Rust

  • Comparabil cu C și C++, dar pune accentul pe eliminarea unor categorii întregi de erori
  • Relativ nou, deci nu e încă popular în toate domeniile
    • Firefox, Chrome
    • Windows, Linux, Android
    • Amazon (AWS Lambda), Microsoft (Azure Boost), Google, Meta, Discord, Canonical, Huawei, OpenAI, npm, …

Hello, world!


                    $ rustup install stable
                    $ cargo new hello
                        Created binary (application) `hello` project
                    $ cargo add gdal
                    

                        .
                        ├── Cargo.lock
                        ├── Cargo.toml
                        └── src
                            └── main.rs
                    

                        [package]
                        name = "hello"
                        version = "0.1.0"
                        edition = "2024"

                        [dependencies]
                        gdal = "0.18.0"
                    

                        use gdal::{Dataset, errors::GdalError};

                        fn main() -> Result<(), GdalError> {
                            let ds = Dataset::open("in.tif")?;
                            for (index, band) in ds.rasterbands().enumerate() {
                                let band = band?;
                                println!("Band {}: {}x{}", index + 1, band.x_size(), band.y_size());
                            }
                            Ok(())
                        }
                    

                        $ cargo run
                        Compiling hello v0.1.0 (hello)
                        […]
                        Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.06s
                         Running `target/debug/hello`
                         Band 1: 10980x10980
                    

Sisteme de tipuri

  • Valorile dintr-un program Rust au asociate tipuri de date (numere, text etc.)
  • Tipurile sunt verificate la compilarea programului (similar cu C fără conversii implicite, nu Python)
  • Operațiile au nevoie de tipuri de date compatibile (similar cu C, Python, nu JavaScript)
  • Strong, static typing
  • Tipurile variabilelor locale pot fi în general deduse de compilator

Tipuri de date în Rust

  • i8, u8, …, f32, f64, char
  • [i32; 16], Vec<i32>, &[i32]
  • String, &str
  • struct Pair<T> { first: T, second: T }
  • (i32, String)
  • enum Result<T, E> { Ok(T), Err(E) }
  • &T, &mut T
  • *const T, *mut T

Structuri, traits

  • Ca sintaxă, structurile sunt asemănătoare claselor din alte limbaje: vec.len()
  • Diferența majoră: nu există moștenire
  • 
                                struct Cat {
                                    name: String,
                                }
    
                                trait SayHello {
                                    fn say_hello(&self);
                                }
    
                                impl SayHello for Cat {
                                    fn say_hello(&self) {
                                        println!("Meow! I'm {}", self.name);
                                    }
                                }
    
                                let cat = Cat { name: "Matilda".to_string() };
                                cat.say_hello();
                            
  • Asemănătoare interfețelor, dar implementarea este separată de definiție

De ce Rust?

  • Microsoft: 70% din problemele de securitate din Windows sunt cauzate de gestiunea greșită a memoriei
  • Google: 70% din problemele de securitate din Chrome sunt cauzate de gestiunea greșită a memoriei
  • Mozilla: 74% din problemele de securitate din Firefox sunt cauzate de gestiunea greșită a memoriei

De ce Rust?

  • Limbaj modern
  • Tooling prietenos (compilator, linter, formatter, suport pentru IDE-uri)
  • Cod performant
  • Biblioteci
  • Accesibilitate
  • Documentație
  • Securitate

De ce Rust?

  • Poate fi folosit ca Python, performanță comparabilă cu C și C++
  • Compilatorul previne nu numai NameError, ci și erori de logică
  • Fără* SIGSEGV etc.

* exceptând codul unsafe

Abordarea Rust

  • Technology from the past come to save the future from itself
  • Memoria este gestionată automat (ownership, reference counting)
  • Accesele la array-uri și vectori sunt verificate
    • Pot fi evitate folosind iteratori și anumite idiomuri
  • Tipurile de date declară explicit dacă pot fi mutate de pe un fir de execuție pe altul, sau dacă pot fi folosite concurent

Aliasing


                            void f(int *p, int *q) {
                                *p += *q;
                                *p += *q;
                            }

                            void g(int *p, int *q) {
                                *p += *q * 2;
                            }

                            f(&x, &x);
                        
  • f și g nu sunt echivalente deoarece p și q pot indica spre aceeași valoare
  • Eroare de compilare în Rust

Borrow checker

  • Referințele sunt asociate unor regiuni care determină durata lor de validitate
  • Compilatorul se asigură că valorile trăiesc cel puțin la fel de mult ca referințele către ele
  • 
                                struct Person<'a> {
                                    first_name: &'a str,
                                    last_name: &'a str,
                                }
    
                                let buffer = "Ion Popescu";
                                let person = Person {
                                    first_name: &buffer[..3],
                                    last_name: &buffer[4..],
                                };
                            
  • Rust are două tipuri de referințe: partajate și exclusive

Performanță


                        class ::String
                          def blank?
                            /\A[[:space:]]*\z/ == self
                          end
                        end
                    

                        extern "C" fn fast_blank(buf: &Buf) -> bool {
                            buf.as_slice().chars().all(|c| c.is_whitespace())
                        }
                    

                                static VALUE
                                rb_str_blank_as(VALUE str)
                                {
                                  rb_encoding *enc;
                                  char *s, *e;

                                  enc = STR_ENC_GET(str);
                                  s = RSTRING_PTR(str);
                                  if (!s || RSTRING_LEN(str) == 0) return Qtrue;

                                  e = RSTRING_END(str);
                                  while (s < e) {
                                    int n;
                                    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);

                                    switch (cc) {
                                      case 9:
                                      case 0xa:
                                      case 0xb:
                                      case 0xc:
                                      case 0xd:
                                      case 0x20:
                                      case 0x85:
                                      case 0xa0:
                                      case 0x1680:
                                      case 0x2000:
                                      case 0x2001:
                                      case 0x2002:
                                      case 0x2003:
                                      case 0x2004:
                                      case 0x2005:
                                      case 0x2006:
                                      case 0x2007:
                                      case 0x2008:
                                      case 0x2009:
                                      case 0x200a:
                                      case 0x2028:
                                      case 0x2029:
                                      case 0x202f:
                                      case 0x205f:
                                      case 0x3000:
                                          /* found */
                                          break;
                                      default:
                                          return Qfalse;
                                    }
                                    s += n;
                                  }
                                  return Qtrue;
                                }
                            

                                #define STR_ENC_GET(str) rb_enc_from_index(ENCODING_GET(str))

                                static VALUE
                                rb_str_blank(VALUE str)
                                {
                                  rb_encoding *enc;
                                  char *s, *e;

                                  enc = STR_ENC_GET(str);
                                  s = RSTRING_PTR(str);
                                  if (!s || RSTRING_LEN(str) == 0) return Qtrue;

                                  e = RSTRING_END(str);
                                  while (s < e) {
                                    int n;
                                    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);

                                    if (!rb_isspace(cc) && cc != 0) return Qfalse;
                                    s += n;
                                  }
                                  return Qtrue;
                                }
                            
Ruby 946K iter/s
C 10.5M iter/s
Rust 11M iter/s

Sursă: http://intorust.com/tutorial/why-rust/

Copernicus Sentinel-2

  • Constelație de trei sateliți, ESA
  • Revizitare la ~5 zile (⚠️ ☁️)
  • Scene de ~110 × 110 km²
  • Suntem în celula 35TMK
  • Datele sunt disponibile pentru oricine, fără restricții sau cost

Sentinel-2 Multi-Spectral Instrument

  • 4 benzi @ 10 m (RGB, NIR)
  • 6 benzi @ 20 m (NIR, SWIR)
  • 3 benzi @ 60 m (aerosoli, vapori de apă, cirrus)
Sursă: Copernicus Sentinel-2A Calibration and Products Validation Status

Detecția arborilor vegetației din spațiu

  • Vegetația este verde, dar la fel pot părea și apa, umbrele, acoperișurile etc.
  • De ce sunt plantele verzi?
  • Ce înseamnă verde?
  • Lumina soarelui e albă și conține toate culorile
  • Obiectele verzi reflectă verdele și absorb celelalte culori

Clorofila — NDVI

  • Vegetația este verde datorită clorofilei
  • Clorofila absoarbe lumina violet/albastră și roșie
  • … și reflectă verdele și infraroșul
  • Normalized Difference Vegetation Index (NDVI) NDVI = NIR Red NIR + Red
Sursă: Wikipedia

Interludiu: Compozit RGB

Sentinel-2 RGB, 35TMK, 2024-06-23

Interludiu: Compozit NIR-R-G

Sentinel-2 NIR-R-G, 35TMK, 2024-06-23

NDVI

  • Arată vegetația activă fotosintetic
  • Valori în -1..1, cele negative mai puțin interesante
  • Fără vegetație: 0.2 , vegetație rară: 0.3 , vegetație densă: >0.5
Sentinel-2 NDVI, 35TMK, 2024-06-23

Fenologia culturilor

Sursă: TimeMatch: Unsupervised Cross-Region Adaptation by Temporal Shift Estimation

NDVI (compozit, trei date)

  • Roșu / magenta: NDVI mare primăvara, culturi timpurii
  • Verde: NDVI mare vara, culturi de vară
  • Galben: NDVI mare primăvara și vara, copaci
  • Albastru / cian: NDVI mare vara și toamna, culturi irigate
  • Negru: vegetație absentă
Sentinel-2 NDVI compozit, 35TMK, 2024-04-14, 2024-07-08, 2024-10-26

Citirea rasterelor în Rust

  • Folosim GDAL, cea mai cunoscută bibliotecă de I/E pentru date geospațiale
  • Suport pentru accesul concurent la date, nou în GDAL 3.10, disponibil în curând în biblioteca Rust
  • Putem citi regiuni din intrare, pentru a economisi memoria
                                use gdal::{Dataset, GdalOpenFlags::*};

let open_flags = GDAL_OF_RASTER | GDAL_OF_THREAD_SAFE;
let ds = Dataset::open_ex(
    path,
    DatasetOptions {
        open_flags,
        ..Default::default()
    },
)?;

let mut data = ds
    .rasterband(1)?
    .read_as(window, window_size, window_size, None)?
    .to_array()?;
data.mapv_inplace(|x| x + offset);


fn ndvi(b4: i16, b8: i16, scl: u8) -> f32 {
    if matches!(scl, 0 | 1 | 6 | 8 | 9 | 10 | 11) {
        return f32::NAN;
    }
    let (b4, b8) = (b4 as f32, b8 as f32);
    if b8 + b4 == 0.0 {
        f32::NAN
    } else {
        (b8 - b4) / (b8 + b4).clamp(-1.0, 1.0)
    }
}                                
                            

Organizarea rasterelor

  • În general, rasterele sunt organizate într-unul din două moduri:
    • Tiles (blocuri)
    • Strips (linii)
  • Organizarea în blocuri e mai eficientă deoarece facilitează procesarea regiunilor dreptunghiulare
  • Valabil mai ales pentru vizualizare
  • Sentinel-2 folosește JPEG 2000 (organizat în blocuri, fișiere mici, dar decodare lentă)
Sursă

Schiță a procesării

  1. Interogăm un catalog de produse pentru un interval de timp dorit
  2. Parcurgem fiecare bloc din fiecare intrare
    • primul bloc din fiecare intrare
    • al doilea bloc din fiecare intrare
  3. Citim regiuni de pe mai multe fire de execuție, le trimitem pe un canal
  4. Un fir de execuție preia datele, calculează valoarea medie a NDVI-ului pentru fiecare pixel, și scrie blocul în fișierul de ieșire
  5. Acumulăm suma și numărul de valori valide, pe măsură ce citim regiunile fără a păstra datele în memorie

Rezultate

  • 2 minute pentru 215 produse, de-a lungul unui an
  • 18.5 GB pe disc, 58.3 GB necomprimat
  • 6-8 GB RAM, în funcție de dimensiunea regiunilor și numărul de fire de execuție
  • Practic tot timpul petrecut în OpenJPEG, dar poate satura 32 de procesoare logice
  • Pe o mașină virtuală din cloud, aprilie - noiembrie, 100 de produse, 11 min per grup de celule
  • 3 min procesarea, 8 min deschiderea fișierelor
  • După tăiere, mozaicare, conversie la 8 biți și compresie: 8 GB

Demo

Îmbunătățiri posibile

  • NDVI median, nu mediu (nu poate fi calculat online)
  • Combinarea cu alți indici (EVI)
  • Clasificare multi-dată
  • Folosirea unui set de date potrivit pentru clasificare și validare (ESA CCI-LC)

GeoRust

  • Organizație pentru proiecte GIS
  • Geometrie: geo, geos*, robust
  • Indexuri spațiale (k-NN): rstar, geo-index
  • Transformări de coordonate: proj*, geodesy
  • Formate de date: gdal*, netcdf*, gpx, geojson, wkt, wkb, kml, osm, transit, shapefile, STAC, PgSTAC, GeoTIFF
  • Utilitare: geozero, geocoding, geohash, polyline
  • Alte unelte: rinex

*interfețe cu biblioteci C
nu sunt sub umbrela GeoRust
mai multe implementări disponibile, unele în GeoRust, unele nu

Resurse

Interoperabilitate

  • Rust poate folosi și expune ABI-ul C
  • C: bindgen
  • C++: autocxx, …
  • Python: pyo3
  • R: extendr-api
  • Node.js: neon
  • Kotlin, Swift, Python, Ruby: maturin
  • WASM, Go, C#, Dart, Java
Logo-ul GeoRust, CC-BY
Logo-ul GDAL, MIT
Logo-ul OSGeo
Logo-ul Rust, CC-BY

https://github.com/lnicola/geo-spatial-rust-2025

@lnicola@mapstodon.space

lnicola@dend.ro