Rust 从入门到摔门而出门 - 字符串类型
Rust 中的字符串和其他的有所不同。
请看下面代码:
fn main() {
let str = "Rust";
console_log(str);
}
fn console_log(str: String) {
println!("Hello, {}!", str);
}
上面的代码会报错,错误如下
|
8 | console_log(str);
| ----------- ^^^- help: try using a conversion method: `.to_string()`
| | |
| | expected `String`, found `&str`
| arguments to this function are incorrect
|
note: function defined here
--> src/main.rs:11:4
|
11 | fn console_log(str: String) {
| ^^^^^^^^^^^ -----------
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 1 previous error
编译器提示console_log函数需要一个String类型的数据,但是传入确是&str类型的;
解决方法:
fn main() {
let str = String::from("hello world");
console_log(str);
}
fn console_log(str: String) {
println!("Hello, {}!", str);
}
或者
fn main() {
let str = "Rust";
console_log(str);
}
fn console_log(str: &str) {
println!("Hello, {}!", str);
}
字符串类型种类
在Rust中有六种字符串类型:
&str: 这是一个字符串切片,它是固定大小的,并且不能改变。它通常用于函数参数,让我们可以接收任何类型的字符串。String: 这是一个可增长的、可改变的、拥有所有权的、UTF-8 编码的字符串类型。它通常用于需要改变或者增长字符串的情况。OsStr、OsString: 这两种类型用于与操作系统进行交互。OsStr 是不可变的,而 OsString 是可变的。它们并不一定是 UTF-8 编码的。CString、CStr:这两种类型用于与 C 语言库交互。CString 是可变的,而 CStr 是不可变的。它们是 null 结尾的,这与其他 Rust 字符串类型不同。
在 开发中常用的类型 &str 和 String,两者的主要区别在于:
-
所有权:
String是一个拥有所有权的类型,当String类型的变量离开其作用域时,Rust 将自动释放其内存。而&str类型的变量则没有所有权,它只是一个对现有字符串数据的引用。 -
可变性:
String是可变的,可以通过push_str()和push()等方法来修改。而&str是不可变的。 -
存储位置:
String类型的数据存储在堆上,而&str类型的数据存储在栈上。 -
性能:由于
&str
是对现有字符串的引用,因此在处理大量字符串数据时,使用 &str 类型可能会比使用 String 类型更高效。
字符串切片
字符串切片(&str)是对 String 的引用,它是不可变的,也就是说你不能改变它的内容。字符串切片的主要用途是作为函数的参数,这样函数就可以接受任何形式的字符串。
你可以通过索引操作来获取一个字符串的切片。例如:
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
创建切片语法: [开始索引..终止索引]。
let hello = &s[..5];:开始索引不填写默认从0开始let world = &s[6..];:终止索引不填写默认到最后一个字节
注意: 字符串切片中如果是中文等特殊语言需要注意,一个中文在 UTF-8 中占用三个字节。
let s = String::from("你好");
let world = &s[0..1]; // Error
上面代码执行会报错,错误信息如下:
warning: unused variable: `world`
--> src/main.rs:8:9
|
8 | let world = &s[0..1]; // Error
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_world`
|
= note: `#[warn(unused_variables)]` on by default
warning: `playground` (bin "playground") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.10s
$ ./playground
thread 'main' panicked at src/main.rs:8:19:
byte index 1 is not a char boundary; it is inside '你' (bytes 0..3) of `你好`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
字符串操作
Rust 中 添加、删除、替换、插入、连接。
添加(PUSH)
在可变字符串尾部添加内容。
fn main() {
let mut str = String::from("Hello ");
str.push_str("rust");
println!("追加字符串 push_str() -> {}", str);
//输出: 追加字符串 push_str() -> Hello rust
str.push('!');
println!("追加字符 push() -> {}", str);
//输出: 追加字符 push() -> Hello rust!
}
插入(insert)
插入方法 insert 和 insert_str,参数 (插入位置, 插入内容)
fn main() {
let mut str = String::from("Hello rust!");
str.insert(5, ','); // 插入位置, 内容
println!("插入字符 insert() -> {}", str);
// 输出:插入字符 insert() -> Hello, rust!
str.insert_str(6, " I like");// 插入位置, 内容
println!("插入字符串 insert_str() -> {}", str);
// 输出:插入字符串 insert_str() -> Hello, I like rust!
}
替换
把字符串的某个内容替换。
replace方法有两个参数,第一个填入要替换的字符串,第二个填入替换成的字符串, 返回新的字符串。
fn main() {
let string_replace = String::from("Hello, rust. rust is the best language!");
let new_string_replace = string_replace.replace("rust", "RUST");
println!("输出: {}",new_string_replace);
//输出: Hello, RUST. RUST is the best language!
}
replacen方法接收三个参数,第一个填入要替换的字符串,第二个填入替换成的字符串,第三个参数表示替换的个数,返回新的字符串。
fn main() {
let string_replace = String::from("Hello, rust. rust is the best language!");
let new_string_replace = string_replace.replacen("rust", "RUST", 1);
println!("输出: {}",new_string_replace);
//输出: Hello, RUST. rust is the best language!
}
replace_range方法接收两个参数,第一个参数是要替换字符串的范围(Range),第二个填入替换成的字符串,该方法是直接操作原来的字符串,不会返回新的字符串。该方法需要使用 mut 关键字修饰。
fn main() {
let mut string_replace_range = String::from("I like rust!");
string_replace_range.replace_range(7..8, "R");
println!("输出: {}",string_replace_range);
//输出: I like Rust!
}
删除
与字符串删除相关的方法有 4 个,它们分别是 pop(),remove(),truncate(),clear()。这四个方法仅适用于 String 类型。
pop: 删除并返回字符串的最后一个字符, 变量必须是可变变量(mut)。
fn main() {
let mut string_pop = String::from("Rust 是最棒的语言!");
let p1 = string_pop.pop();
dbg!(p1);
// p1 = Some(
// '!',
// )
let p2 = string_pop.pop();
dbg!(p2);
// p2 = Some(
// '言',
// )
println!("输出: {}", string_pop)
// 输出: Rust 是最棒的语
}
remove: 删除指定位置的字符索引开始位置 参数传入指定字符,非合法字符串 边界会报错
fn main() {
let mut string_remove = String::from("你好吗Rust是最好的语言");
// 删除第一个汉字 你
string_remove.remove(0);
// 下面代码会发生错误 因为一个汉字字符3
// string_remove.remove(1);
// 删除汉字 好,上面的你删除了字符串的切片下标改了
string_remove.remove(0);
// 如果想删除 R, 参数3 因为汉字3个字符
string_remove.remove(3);
println!("输出: {}", string_remove)
// 输出: ust是最好的语言
}
truncate: 删除字符串中从指定位置开始到结尾的全部字符 参数传入指定字符,非合法字符串 边界会报错
fn main() {
let mut string_remove = String::from("你好吗Rust是最好的语言");
// 删除第一个汉字后面的汉字
string_remove.truncate(3);
println!("输出: {}", string_remove)
// 输出: 你
}
4.clear:清空字符串内容。
fn main() {
let mut string_remove = String::from("你好, Rust是最好的语言");
// 清空字符串
string_remove.clear();
println!("输出: {}", string_remove)
// 输出:
}
5.+ 和 +=: 连接字符串。
fn main() {
let string_test1 = String::from("你好,");
let string_test2 = String::from("Rust是最好的语言");
let mut string_test3 = string_test1 + &string_test2;
string_test3 += "!";
println!("输出: {}", string_test3)
// 输出: 你好,Rust是最好的语言!
}
字符串
在 Rust 中 String类型,为了是一个可增长、可变的、拥有所有权的字符串类型。它在堆上分配内存,因此可以动态地改变大小。 当你需要一个可以修改的字符串时,通常会使用 String 类型。
在 Rust 中 &str 是一个固定大小的字符串切片,通常用于引用字符串的一部分。它是不可变的,也就是说你不能改变它的内容。这是因为 &str 是对现有数据的引用,而不是拥有自己的数据。如果它被允许改变引用的数据,那么任何其他引用相同数据的代码都可能会受到影响,这会导致错误和不一致。
这种设计好处:&str 非常适合用于函数参数,因为你可以安全地传递对数据的引用,而不必担心数据会被修改。同时,因为 &str 是固定大小的,所以它可以在栈上存储,这比在堆上分配内存更快
String 和 &str 的设计选择反映了 Rust 的关注点:安全性、并发性和内存效率。