Range în Rust

Intervalul a..b: toate valorile de la a inclusiv până la b exclusiv. Este iterator, se folosește în for și la tăierea slice-urilor.

Sintaxă
pub struct Range<Idx> {
    pub start: Idx,
    pub end: Idx,
}

a..b    // Range: de la a inclusiv la b exclusiv
a..=b   // RangeInclusive: include și b
a..  ..b  ..

Ce face Range

a..b creează un Range cu câmpurile start și end. Pentru numere întregi și caractere este un iterator: for i in 0..5 dă 0, 1, 2, 3, 4 (fără 5). a..=b creează un RangeInclusive, care include și capătul: 1..=5 dă 1, 2, 3, 4, 5.

Intervalele au metode utile: contains(&x), is_empty(), len() (pentru Range de întregi), plus toate metodele de iterator (rev, step_by, sum, map). Un interval cu începutul mai mare decât sfârșitul (5..2) este gol, nu invers.

Formele fără capăt servesc la tăiere și la căutări: &v[2..], &text[..3], &v[..], map.range(10..). a.. pe numere este un iterator infinit.

Parametri

Parametru Descriere
start Începutul intervalului (inclus).
end Sfârșitul intervalului (exclus la .., inclus la ..=).

Ce returnează

Un Range (sau RangeInclusive, RangeFrom, RangeTo, RangeFull).

Exemple

Exemplul 1: Exclusiv, inclusiv, invers, cu pas

fn main() {
    let a: Vec<u32> = (1..5).collect();
    let b: Vec<u32> = (1..=5).collect();
    println!("1..5  = {:?}", a);
    println!("1..=5 = {:?}", b);
    println!("5..2 are {} elemente", (5..2).count());
    println!("inversat: {:?}", (1..=5).rev().collect::<Vec<_>>());
    println!("din 10 în 10: {:?}", (0..=50).step_by(10).collect::<Vec<_>>());
    println!("litere: {}", ('a'..='f').collect::<String>());
}

Rezultat

1..5  = [1, 2, 3, 4]
1..=5 = [1, 2, 3, 4, 5]
5..2 are 0 elemente
inversat: [5, 4, 3, 2, 1]
din 10 în 10: [0, 10, 20, 30, 40, 50]
litere: abcdef

Exemplul 2: contains, len și câmpurile start/end

fn main() {
    let ore_deschidere = 9..18;
    for ora in [8, 9, 17, 18] {
        println!("ora {ora}: {}", if ore_deschidere.contains(&ora) { "deschis" } else { "închis" });
    }
    println!("interval: {}..{}, {} ore", ore_deschidere.start, ore_deschidere.end, ore_deschidere.len());

    let nota_trecere = 5..=10;
    println!("7 e notă de trecere? {}", nota_trecere.contains(&7));
    println!("suma 1..=100 = {}", (1..=100).sum::<u32>());
}

Rezultat

ora 8: închis
ora 9: deschis
ora 17: deschis
ora 18: închis
interval: 9..18, 9 ore
7 e notă de trecere? true
suma 1..=100 = 5050

Exemplul 3: Tăierea slice-urilor și a textelor

fn main() {
    let note = [10, 9, 8, 7, 6, 5];
    println!("{:?}", &note[1..4]);
    println!("{:?}", &note[..2]);
    println!("{:?}", &note[4..]);
    println!("{:?}", &note[..]);

    let cnp = "1990101123456";
    println!("anul nașterii: 19{}", &cnp[1..3]);
}

Rezultat

[9, 8, 7]
[10, 9]
[6, 5]
[10, 9, 8, 7, 6, 5]
anul nașterii: 1999

Greșeli frecvente

  • Uiți că a..b nu include b: for i in 1..10 merge până la 9. Pentru a include capătul, scrie 1..=10.
  • Scrii 10..0 pentru numărătoare inversă: intervalul e gol. Scrie (0..10).rev() sau (1..=10).rev().
  • Tai un text la o poziție din mijlocul unei litere cu diacritice: &"Iași"[..3] oprește programul (byte index 3 is not a char boundary). Pozițiile din &s[a..b] sunt în octeți.
  • Încerci (0.0..1.0).step_by(..): intervalele de f64 nu sunt iteratori. Iterează pe întregi și împarte: (0..10).map(|i| i as f64 / 10.0).

Comenzi înrudite

  • Iterator::step_by: Produce primul element și apoi fiecare al n-lea: sare peste elemente cu un pas fix.
  • Iterator::rev: Inversează direcția unui iterator: produce elementele de la ultimul la primul, fără să copieze nimic.
  • IntoIterator: Trait-ul tipurilor care pot fi transformate într-un iterator; este ce folosește bucla for pentru a parcurge o valoare.
  • BTreeMap::range: Parcurge doar perechile cu cheile într-un interval (a..b, a..=b, a.., ..b), în ordine crescătoare.
  • Vec::drain: Scoate din vector elementele dintr-un interval de poziții și le dă înapoi ca iterator, mutate (fără copiere).