我正在写一个函数,它需要一个更大整数的各个数字来对其执行操作.

我试过以下方法:

fn example(num: i32) {
    // 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());
    for digit in digits {
        println!("{}", digit)
    }
}

但借书判断人员表示,这根绳子的生命周期 不够长:

error[E0716]: temporary value dropped while borrowed
 --> src/lib.rs:3:18
  |
3 |     let digits = num.to_string().chars().map(|d| d.to_digit(10).unwrap());
  |                  ^^^^^^^^^^^^^^^                                         - temporary value is freed at the end of this statement
  |                  |
  |                  creates a temporary which is freed while still in use
4 |     for digit in digits {
  |                  ------ borrow later used here
  |
  = note: consider using a `let` binding to create a longer lived value

以下方法确实有效:

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

但这看起来更加做作.

有没有更好、更自然的方法?

推荐答案

但是借钱人说绳子的生命周期 不够长.

那是因为它是doesn't.您没有使用迭代器,所以digits的类型是

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

也就是说,一个尚未计算的迭代器,其中包含对已分配字符串的引用(Chars中的未命名生存期'_).但是,由于该字符串没有所有者,因此会在语句末尾删除它;在使用迭代器之前.

所以,是的,因为Rust ,它阻止了免费使用后的错误!

使用迭代器将"解决"这个问题,因为对分配的字符串的引用不会试图比分配的字符串活得更长;它们都在声明末尾结束:

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

如果要返回迭代器,可以将Vec转换回迭代器:

fn digits(num: usize) -> impl Iterator<Item = u32> {
    num.to_string()
        .chars()
        .map(|d| d.to_digit(10).unwrap())
        .collect::<Vec<_>>()
        .into_iter()
}

至于另一种解决方案,有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);
}

然而,您可能希望为负数添加所有特殊情况逻辑,测试也不是一个坏主意.

您可能还需要一个花哨的裤子迭代器版本:

fn digits(mut num: usize) -> impl Iterator<Item = usize> {
    let mut divisor = 1;
    while num >= divisor * 10 {
        divisor *= 10;
    }

    std::iter::from_fn(move || {
        if divisor == 0 {
            None
        } else {
            let v = num / divisor;
            num %= divisor;
            divisor /= 10;
            Some(v)
        }
    })
}

或者完全自定义的类型:

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);
}

另见:

Rust相关问答推荐

返回的future 不是`发送`

在Rust中,在实现特征`Display`时,如何获取调用方指定的格式?

是否提供Bundle 在可执行文件中的warp中的静态文件?

Rust面向对象设计模式

write_buffer 不写入缓冲区而是输出零 WGPU

Rust 如何返回大类型(优化前)?

我可以禁用发布模式的开发依赖功能吗?

全面的 Rust Ch.16.2 - 使用捕获和 const 表达式的 struct 模式匹配

str 和 String 的 Rust 生命周期

rust tokio::spawn 在 mutexguard 之后等待

如何使返回 XMLError 的方法与 anyhow::Error 兼容?

预期的整数,找到 `&{integer}`

为什么拥有 i32 所有权的函数需要它是可变的?

只有一个字符被读入作为词法分析器的输入

如何将切片推入数组?

在空表达式语句中移动的值

tokio async rust 的 yield 是什么意思?

在使用大型表达式时(8k 行需要一小时编译),是否可以避免 Rust 中的二次编译时间?

如何从 Rust 中不同类型的多个部分加入 Path?

如何重写这个通用参数?