今天在工作中遇到一个问题,string调用truncate()接口panic了,报错信息大致如下:thread '0' panicked at 'assertion failed: self.is_char_boundary(new_len)', liballoc/string.rs:1121:13
我的代码如下:
示例1:
fn main() {
let mut s = String::from("hello 中国");
s.truncate(7); //获取前7个字节
println!("s:{}", s);
}
------------------------------------------------------------------------
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/playground`
thread 'main' panicked at 'assertion failed: self.is_char_boundary(new_len)', src/liballoc/string.rs:1123:13
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
当然原始代码不是这个,但是原理是一样的。这里的问题出现在字符串中的中文(纯英文字符不会出现panic)。原因是,一个汉字所在字节数为非1 byte,当去截取slice的中字符时,字符边界判断导致panic了。
一开始怀疑是truncate()接口的问题,但后来发现并不是truncate本身的问题,所有涉及到slice中截取中文字符都会容易导致panic,不信看下面例子:
示例2:
fn main() {
let a = "abcd早";
let b = &a[..5];
println!("b={}", b);
}
-------------------------------------------------------------------------
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.51s
Running `target/debug/playground`
thread 'main' panicked at 'byte index 5 is not a char boundary; it is inside '早' (bytes 4..7) of `abcd早`', src/libcore/str/mod.rs:2027:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
再看如下例子:
示例3:
fn main() {
let a = "abcd早";
let b = &a[..3];
println!("b={}", b);
}
--------------------------------------------------------------
输出结果:
b=abc
示例4:
fn main() {
let a = "abcd早";
let b = &a[..7];
println!("b={}", b);
}
-------------------------------------------------------------
输出结果:
b=abcd早
示例3与示例2的区别在于,截取的字节数不同。示例3截取前3个字符均是英文,而示例4正好截取到了中文“早”字的字符边界(“早”字占4个字节)。
那么实际生产环境中很难保证我们要截取的slice中没有中文字符,任意截取不能保证正好是字符边界,那该怎么办?
网上有人提到先把slice转换为chars的vector,然后再调用truncate()之类的,但是觉得这样太消耗性能,所以我得方法是:
// 首先判断给出的index是不是字符边界,否则向后找到字符边界所在位置
fn find_char_boundary(s: &str, index: usize) -> usize {
if s.len() <= index {
return index;
}
let mut new_index = index;
while !s.is_char_boundary(new_index) {
new_index += 1;
}
new_index
}
fn main() {
let mut s = String::from("hello 中国");
let idx = find_char_boundary(&s, 7); //实际获取到的idx=9
s.truncate(idx);
println!("idx:{}, s:{}", idx, s);
}
-------------------------------------------------------------------
输出结果:
idx:9, s:hello 中
好了,以上就是对自己在rust编程中遇到的问题,做一个总结与备忘,希望对有需要的人也能够有所帮助!