我们知道,Rust中字符串的切片容易会引起panic,千万要注意。主要是,Rust的切片,切的其中字节,这个说法有些抽象,如何能理解呢?
首先,对一个字符串,你认为其字符数和长度是一个什么样的概念?相等,不相等?
比如,对于ascii码字符串,
let same_string ="abc";
println!("chars:{}, length:{}",same_string.chars().count(),same_string.len());// chars :3,length:3
当然,不都是这样的。
let other_string ="忠犬ハチ公";
println!("chars:{}, length:{}",other_string.chars().count(),other_string.len());// chars :5,length:15
所以,最重要的是,Rust的切片,切的是字节,而不是字符。
一、Rust的切片操作
//String
let t1 = "love".to_string();
println!("String =>t1:{:?}", &t1[2..]);//必须带&, =>ve
// &str
let t2 = "love";
println!("&str =>t2:{:?}", &t2[2..]);//必须带& =>ve
好象看起来,很正常呀,没什么问题的。
如果碰到的全是a…z之类构成的,是可能没问题的,但其它的呢?
二、有没有意外?
既然是危险的操作,危险就是必然的。
// 一个“不正常”的例子
let love_china = "忠犬ハチ公";
println!("{:?}=>bytes :{:?}", love_china, love_china.bytes().len());
//println!("{:?}", love_china.len_utf8());
for i in love_china.chars() {
println!("{:?} =>bytes :{:?}", i, i.len_utf8());
}
println!("love_china[0] :{:?}", love_china.chars().nth(0));
// 一个正常的例子
let normal = "love";
println!("=>{:?} =>bytes: {:?}", normal, normal.bytes().len());
for i in normal.chars() {
println!("{:?}, =>bytes:{:?}", i, i.len_utf8());
}
println!("normal[0] :{:?}", normal.chars().nth(0).unwrap());
output:
"忠犬ハチ公"=>bytes :15
'忠' =>bytes :3
'犬' =>bytes :3
'ハ' =>bytes :3
'チ' =>bytes :3
'公' =>bytes :3
love_china[0] :Some('忠')
=>"love" =>bytes: 4
'l', =>bytes:1
'o', =>bytes:1
'v', =>bytes:1
'e', =>bytes:1
normal[0] :'l'
发现什么没有?
println!("love_china len:{:?}", love_china.len());// => len =15,而不是5!
知道问题所在了吧…….
(1) ‘忠’ =>bytes :3.
也就是说,’忠’字符却占3个字节,如果你去切1个字节,还会报什么呢,Rust只能panic了。
let dd1 = &love_china[0..3];//切前3个字节=>'忠'.
println!("dd:{:?}", dd);
如果&love_china[0..2]=>切前2个字节,则会panic!.
(2) ‘l’, =>bytes:1。
象“love”其它的每个字符,都只占了1个字节,形成了字符位和字节位的刚好重合,给人一种“安全”的错觉。
三、正确的打开方式
1、单个字符的取值
下面是推荐的取字符串中正确的打开方式:.chars().nth(0)。
println!("love_china[0] :{:?}", love_china.chars().nth(0));
那如果要取其中的两个字符呢? 思考一下…….
2、多个字符的取值
let tt = "我爱工作,rust,julia!";
let t = tt.chars().into_iter().map(|x| x.to_string()).collect::<Vec<_>>();
println!("t:{:?}", t);
let ww: String = t[1..3].concat();
let qq: String = t[1..3].join("");
println!("ww:{:?} qq:{:?}", ww, qq);