Rust 中的字符是 Unicode 类型,因此每个字符占据 4 个字节内存空间,但字符串不一样,字符串是 UTF-8 编码,也就是字符串中的字符所占的字节数是变化的(1 - 4)。
常见的字符串有两种:
- str,通常是引用类型,
&str
,即字符串字面常量,字符串切片。 - std::string::String
类型&str
的变量是被硬编码的,快速而高效,但不可变;类型String
,是可设可变的,它是在堆上的变量,如何管理内存,有的语言用垃圾回收机制(Garbage Collection),标记使用情况并自动清理;而Rust不愿意用GC,既要高性能,又要高安全性,提出变量离开作用域即自动释放其占用的内存,比GC思路更妙。
C语言中清理内存的函数free,要被手动调用;Rust中则是drop(),Rust自动调用。
C++中的Resoure Acquisition Is Initialization,RAII模型。
两种字符串类型互转
&str
转String
:
let a = String::from("hello, world");
"hello, world".to_string();
String
转&str
,引用即可
fn main() {let s = String::from("hello,world!");say_hello(&s);say_hello(&s[..]);say_hello(s.as_str());
}fn say_hello(s: &str) {println!("{}",s);
}
除上述两种类型,Rust标准库还有其他类型的字符串。
字符串不能被直接索引
Rust字符串不允许索引操作。由于不同字符占用字节数不等,考虑操作时间复杂度不能实现O(1)。
//三个字节
let a = "中国人";
//一个字节
let b = "Chinese";
Rust字符串虽然不能索引但可以切片(slice),类似Python等语言中的切片概念。
fn main() {let my_name = "kirk zhang";let first_name = &my_name[0..4];let last_name = &my_name[5..10];println!("{}",first_name);println!("{}",last_name);greet(String::from(my_name));// 尝试my_name[0]报错,不过可以用.chars().nth或.bytes()来实现println!("can str be indexed {:?}",my_name.chars().nth(0));
}fn greet(name: String){println!("hello {}", name);
}
小心字符串切片
注意字符串切片是按字节来的哦,而Rust字符串是UTF-8协议格式,一般1个字符1个字节,但1个中文字符占3个字节;如果切片起止不在字符边界,则有异常。
fn main(){let f = "中国人";let f1 = &f[0..5];println!("watch out, what you got {}",f1);
}
提示:thread ‘main’ panicked at ‘byte index 5 is not a char boundary; it is inside ‘国’ (bytes 3…6) of 中国人
’, src/main.rs:10:15
note: run with RUST_BACKTRACE=1
environment variable to display a backtrace
字符串的操作
替换
replace(要被替换的子串,新字符串),返回结果字符串。
let word = String::from("rust");let new_word = word.replace("r","R");println!("{}", new_word);
插入
insert(位置索引,字符)、insert_str(位置索引,字符串):变量本身变化,声明时要mut。
let word = String::from("rust");let mut new_word = word.replace("r","R");new_word.insert_str(0, "I love ");println!("{}", new_word);
追加
push(字符)
push_str(字符串)
let word = String::from("rust");let mut new_word = word.replace("r","R");new_word.insert_str(0, "I love ");new_word.push('!');println!("{}", new_word);
连接
+
或+=
调用了String的add(self,&str)
方法,其定义要求+后面的参数为字符串字面常量,返回String类型。
或用format! ,适用于 String 和 &str 。format! 的用法与 println! 的用法类似。
let a1 = String::from("tic");
let a2 = String::from("tac");
let a3 = String::from("toe");// 经过下面一行后,a1被释放了,不能再使用!a2、a3依然存在。
let s = s1 + "-" + &s2 + "-" + &s3;
删除
有4个方法,pop()、remove(索引位置)、truncate(起始位置)、clear()
注意删除字符串的索引必须是字符串中字符的边界,否则错误。
fn main() {let mut string_remove = String::from("测试remove方法");println!("string_remove 占 {} 个字节",std::mem::size_of_val(string_remove.as_str()));// 删除第一个汉字string_remove.remove(0);// 下面代码会发生错误// string_remove.remove(1);// 直接删除第二个汉字// string_remove.remove(3);dbg!(string_remove);
}
字符串的字符与字节
例,
for c in "中国人".chars() {println!("{}", c);}for b in "中国人".bytes() {println!("{}", b);}