2017-01-08 3 views
2

Ich schreibe eine Funktion, die die einzelnen Ziffern einer größeren Ganzzahl erfordert, um Operationen auszuführen.Teilen einer ganzen Zahl in einzelne Ziffern

Ich habe versucht, die folgenden:

// I can safely unwrap because I know the chars of the string are going to be valid 
let digits = num.to_string().chars().map(|d| d.to_digit(10).unwrap()); 

Aber die borrow checker sagt der String nicht lange genug lebt.

Die folgende funktioniert:

let temp = num.to_string(); 
let digits = temp.chars().map(|d| d.to_digit(10).unwrap()); 

Aber das sieht irgendwie noch gekünstelt.

Gibt es eine bessere und möglicherweise natürlichere Art, dies zu tun?

Antwort

4

Aber der Border Checker sagt, dass die Zeichenfolge nicht lange genug lebt.

Das ist, weil es nicht ist. Sie verwenden den Iterator nicht, so dass die Art der digits ist

std::iter::Map<std::str::Chars<'_>, <closure>> 

Das heißt, ein noch-zu-sein-bewertet Iterator, die Verweise auf die zugewiesenen String (der nicht namentlich Lebensdauer '_ in Chars) enthält. Da diese Zeichenfolge jedoch keinen Eigentümer hat, wird sie am Ende der Anweisung gelöscht. bevor der Iterator verbraucht ist.

Also, yay für Rust, es verhindert eine Verwendung-nach-Free-Bug!

Die Verwendung des Iterators würde das Problem "lösen", da die Verweise auf die zugewiesene Zeichenfolge nicht versuchen würden, länger als die zugewiesene Zeichenfolge zu leben. sie enden alle am Ende der Aussage:

let digits: Vec<_> = num.to_string().chars().map(|d| d.to_digit(10).unwrap()).collect(); 

Wie für eine alternative Lösung gibt es die math way, stolen from the C++ question:

fn x(n: usize) -> Vec<usize> { 
    fn x_inner(n: usize, xs: &mut Vec<usize>) { 
     if n >= 10 { 
      x_inner(n/10, xs); 
     } 
     xs.push(n % 10); 
    } 
    let mut xs = Vec::new(); 
    x_inner(n, &mut xs); 
    xs 
} 

fn main() { 
    let num = 42; 
    let digits: Vec<_> = num.to_string().chars().map(|d| d.to_digit(10).unwrap()).collect(); 
    println!("{:?}", digits); 
    let digits = x(42); 
    println!("{:?}", digits); 
} 

aber Sie können alle Sonderfall Logik hinzufügen möchten für negative Zahlen und Tests wären keine schlechte Idee.

Vielleicht wollen Sie auch eine Phantasie-Hosen Iterator Version:

struct Digits { 
    n: usize, 
    divisor: usize, 
} 

impl Digits { 
    fn new(n: usize) -> Self { 
     let mut divisor = 1; 
     while n >= divisor * 10 { 
      divisor *= 10; 
     } 

     Digits { 
      n: n, 
      divisor: divisor, 
     } 
    } 
} 

impl Iterator for Digits { 
    type Item = usize; 

    fn next(&mut self) -> Option<Self::Item> { 
     if self.divisor == 0 { 
      None 
     } else { 
      let v = Some(self.n/self.divisor); 
      self.n %= self.divisor; 
      self.divisor /= 10; 
      v 
     } 

    } 
} 

fn main() { 
    let digits: Vec<_> = Digits::new(42).collect(); 
    println!("{:?}", digits); 
} 
+0

Ich sehe nicht, wie das eine Verwendung nach freien Fehler werden würde. Würde es nicht sofort benutzt werden und DANN danach befreit? –

+0

über das Hinzufügen spezieller Fall Tests für negative Zahlen: die Funktion, die dies verwendet 'u32', negative Zahlen werden nie passieren –

+3

@ElectricCoffee: Die Verwendung-nach-Frei kommt, weil' to_string' schafft eine temporäre, die nur so lange lebt wie die Anweisung, in der es erstellt wurde. Allerdings lebt Ihr Iterator "digits", der sich auf dieses temporäre bezieht, wesentlich länger, was inkorrekt ist. Wenn Sie in derselben Anweisung sofort "collect" aufrufen, besteht kein Problem mehr. –

Verwandte Themen