TryFrom în Rust

Conversii care pot eșua și returnează Result: u8::try_from(300) dă Err. Implementarea aduce și try_into().

Sintaxă
impl TryFrom<Sursa> for Tinta {
    type Error = TipEroare;
    fn try_from(v: Sursa) -> Result<Self, Self::Error> { ... }
}

let t = Tinta::try_from(x)?;
let t: Tinta = x.try_into()?;

Ce face TryFrom

TryFrom este varianta lui From pentru conversii care nu reușesc mereu: un i32 negativ nu încape într-un u32, un număr nu este mereu o notă validă. try_from returnează Ok(valoare) sau Err(eroare), iar tipul erorii se alege prin type Error.

La fel ca la From/Into, implementarea lui TryFrom îți dă automat TryInto, deci poți scrie let n: u8 = x.try_into()?;. Ambele trait-uri sunt în preludiul ediției 2021+, deci nu trebuie importate. Pentru numere, try_from este alternativa sigură la as, care taie valoarea pe tăcute.

Parametri

Parametru Descriere
type Error Tipul erorii returnate când conversia eșuează.
try_from(v) Conversia propriu-zisă.

Ce returnează

Result<Tinta, Error>.

Exemple

Exemplul 1: Numere care pot să nu încapă

fn main() {
    println!("{:?}", u8::try_from(200_i32));
    println!("{:?}", u8::try_from(300_i32));
    println!("{:?}", u32::try_from(-5_i64));

    let mare: i64 = 70_000;
    let r: Result<u16, _> = mare.try_into();
    match r {
        Ok(v) => println!("încape: {v}"),
        Err(e) => println!("eroare: {e}"),
    }
    println!("cu as (taie pe tăcute): {}", mare as u16);
}

Rezultat

Ok(200)
Err(TryFromIntError(PosOverflow))
Err(TryFromIntError(NegOverflow))
eroare: number too large to fit in target type
cu as (taie pe tăcute): 4464

Exemplul 2: O notă școlară validă

#[derive(Debug)]
struct Nota(u8);

impl TryFrom<i32> for Nota {
    type Error = String;

    fn try_from(v: i32) -> Result<Self, Self::Error> {
        if (1..=10).contains(&v) {
            Ok(Nota(v as u8))
        } else {
            Err(format!("{v} nu este o notă între 1 și 10"))
        }
    }
}

fn main() {
    for v in [9, 0, 11] {
        match Nota::try_from(v) {
            Ok(n) => println!("ok: {n:?}, valoare {}", n.0),
            Err(e) => println!("eroare: {e}"),
        }
    }
    let n: Result<Nota, _> = 7.try_into();
    println!("{n:?}");
}

Rezultat

ok: Nota(9), valoare 9
eroare: 0 nu este o notă între 1 și 10
eroare: 11 nu este o notă între 1 și 10
Ok(Nota(7))

Greșeli frecvente

  • Folosești as pentru conversii între tipuri numerice cu valori venite din afară: 300 as u8 dă 44, fără eroare. u8::try_from(300) îți spune că nu încape.
  • Implementezi și TryFrom, și From pentru aceeași pereche: Rust are deja un TryFrom automat pentru orice From (cu eroarea Infallible), deci apare conflict.

Comenzi înrudite

  • From: Trait-ul pentru conversii care nu pot eșua: Tinta::from(x). Implementându-l, primești gratuit și x.into().
  • Into: Perechea lui From văzută din partea sursei: x.into() convertește în tipul cerut; util în parametri generici.
  • FromStr: Trait-ul care face un tip „citibil” din text; după implementare, „…”.parse::<Tip>() funcționează.
  • char::from_u32: Creează un char din codul lui Unicode, dacă este valid; altfel returnează None.