rust中slice panicked at 'byte index 5 is not a char boundary' 问题解决办法

今天在工作中遇到一个问题,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编程中遇到的问题,做一个总结与备忘,希望对有需要的人也能够有所帮助!

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值