如何将整数拆分为单个数字?
How do I split an integer into individual digits?
我正在编写一个函数,它需要一个较大整数的各个数字来执行操作。
我试过以下方法:
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());
但这看起来更做作。
是否有更好、可能更自然的方法来做到这一点?
But the borrow checker says the string doesn't live long enough.
那是因为它不会。您没有使用迭代器,所以 digits
的类型是
std::iter::Map<std::str::Chars<'_>, <closure>>
也就是说,一个尚未计算的迭代器,它包含对已分配字符串的引用(Chars
中的未命名生命周期 '_
)。但是,由于该字符串没有所有者,它会在语句末尾被删除;在迭代器被消耗之前。
所以,对 Rust 来说是的,它防止了释放后使用的错误!
使用迭代器会 "solve" 问题,因为对已分配字符串的引用不会尝试比已分配字符串的寿命更长;它们都在语句的末尾结束:
let digits: Vec<_> = num.to_string().chars().map(|d| d.to_digit(10).unwrap()).collect();
如果您想 return 一个迭代器,您可以将 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);
}
另请参阅:
- What is the correct way to return an Iterator (or any other trait)?
我正在编写一个函数,它需要一个较大整数的各个数字来执行操作。
我试过以下方法:
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());
但这看起来更做作。
是否有更好、可能更自然的方法来做到这一点?
But the borrow checker says the string doesn't live long enough.
那是因为它不会。您没有使用迭代器,所以 digits
的类型是
std::iter::Map<std::str::Chars<'_>, <closure>>
也就是说,一个尚未计算的迭代器,它包含对已分配字符串的引用(Chars
中的未命名生命周期 '_
)。但是,由于该字符串没有所有者,它会在语句末尾被删除;在迭代器被消耗之前。
所以,对 Rust 来说是的,它防止了释放后使用的错误!
使用迭代器会 "solve" 问题,因为对已分配字符串的引用不会尝试比已分配字符串的寿命更长;它们都在语句的末尾结束:
let digits: Vec<_> = num.to_string().chars().map(|d| d.to_digit(10).unwrap()).collect();
如果您想 return 一个迭代器,您可以将 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);
}
另请参阅:
- What is the correct way to return an Iterator (or any other trait)?