【Rust自学】8.3. String类型 Pt.1:字符串的创建、更新与拼接
8.3.0. 本章内容
第八章主要讲的是Rust中常见的集合。Rust中提供了很多集合类型的数据结构,这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。
第八章中的集合是存储在堆内存上而非栈内存上的,这也意味着这些集合的数据大小无需在编译时就确定,在运行时它们可以动态地变大或变小。
本章主要会讲三种集合:Vector、String(本文) 和HashMap
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
8.3.1. String对开发者造成的困扰
Rust开发者(尤其是新手)会经常被字符串困扰,原因如下:
- Rust倾向于暴露可能的错误
- 字符串数据结构复杂
- Rust字符串使用了
UTF-8
编码
8.3.2. 字符串是什么
字符串是基于字节(Byte)的集合,并且它提供了一些方法,这些方法能将字节解析为文本。
在Rust的核心语言层面,只有一个字符串类型——字符串切片str
,通常是以借用的情况出现的,也就是&str
。
字符串切片是对存储在其他地方、utf-8
编码的字符串的引用。例如字符串字面值就是直接存储在Rust的二进制文件中,所以它也是一种字符串切片。
String
类型来自于标准库,而不是核心语言。它是一种可增长、可修改、可拥有(获得所有权)的类型,它也采用utf-8
编码。
8.3.3. 字符串到底是指谁?
通常说的字符串就是指String
和&str
这两种类型,而不是其中的一种。这两种类型在标准库里都用的非常频繁,也都是使用了utf-8
编码,但这里主要还是讲String
类型,因为它更复杂。
8.3.4. 其他的字符串类型
Rust标准库还提供了其他的字符串类型,例如:OsString
、OsStr
、CString
、CStr
。但是注意这些类型都是以String
或者是Str
结尾,这就跟之前讲的String
和字符串切片这两种类型的写法又一些关系。
通常来说,以String
结尾的字符串类型是可以获得所有权的,以Str
结尾的类型通常是指可借用 的。
这些不同的字符串类型可以存储不同编码的文本或是在内存中以不同的形式展现(布局不一样)。
某些library crate针对字符串可提供更多的选项,这里就不介绍了。
8.3.5. 创建一个新的字符串(String)
由于String
类型的本质是字节的集合,所以很多Vec<T>
的操作都可以用于String
。
String::new()
可以用来创建一个空的字符串。看个例子:
fn main(){let mut s = String::new();
}
但是一般而言都是使用初始值来创建String
。这个时候就可以使用to_string
方法来创建String
,这个方法可用于实现了Display
trait的类型,包括字符串字面值。如下例:
fn main() { let data = "wjq"; let s = data.to_string(); let s1 = "wjq".to_string();
}
data
是一个字符串字面值,而使用to_string
这个方法把它转为String
类型,存储在s
里。或者也可以直接写字符串字面值,然后写.to_string()
,也就是给s1
赋值的操作。这两个操作是同样的效果。
to_string
也不是唯一的方法,第二种方法是使用String::from
函数。如下例:
let s = String::from("wjq");
这个函数和to_string
方法的效果是一样的。
由于字符串它用的地方非常多,所以Rust提供了很多不同的通用API供我们选择,有些函数可能看着很多余,但实际上它们都有各自的用处。而在实际编码时可以根据喜好来选择。
8.3.6. 更新String
之前提到了,String
类型的大小是可以增减的,其本质是字节的集合,里面的内容也可以修改,它的操作就跟Vector
一样,此外还可以对String
进行拼接。
1. push_str()
首先讲push_str()
,它是一个把字符串切片附加到String
的方法。如下例:
fn main() { let mut s = String::from("6657"); s.push_str("up up"); println!("{}", s);
}
输出:
6657up up
push_str
的签名是push_str(&mut self, string:&str)
,它的参数类型是借用的这个字符串切片,而字符串字面值就是切片,所以"up up"
可以传进去,并且这个方法不会获得参数的所有权,所以传进去的参数不会失效,还能继续使用。
2. push
第二个方法叫push()
,它能把单个字符附加到String
里面。如下例:
fn main() { let mut s = String::from("665"); s.push('7'); println!("{}", s);
}
注意,字符得使用单引号。
输出:
6657
3.+
Rust允许使用+
来拼接字符串。如下例:
fn main() { let s1 = String::from("6657"); let s2 = String::from("up up"); let s3 = s1 + &s2; println!("{}", s3);
}
注意:加号前是字符串类型,加号后得是字符串切片类型。
但在这个例子中实际上加号后的数据类型是&String
而不是&str
。这时因为这里Rust使用了解引用强制转换(deref coercion) 的功能,把&String
类型强制转换为&str
。
当然,因为s2
传进去的是引用,所以s2
在拼接后是仍然有效的,而s1
是把本身的所有权交给了s3
,所以s1
在拼接后就无效了。
输出:
6657up up
4. format!
format!
这个宏可以更加灵活的拼接字符串。如下例:
fn main() { let s1 = String::from("cn"); let s2 = String::from("Niko"); let s3 = String::from("fan club"); let s = format!("{} {} {}", s1, s2, s3); println!("{}", s);
}
使用占位符来代替变量,这点和println!
很像,println!
是把结果进行输出,而format!
则是返回了拼接好的字符串。
输出:
cn Niko fan club
当然使用+
也能实现一样的效果,只不过写起来稍微麻烦一些:
fn main() { let s1 = String::from("cn"); let s2 = String::from("Niko"); let s3 = String::from("fan club"); let s = s1 + " " + &s2 + " " + &s3; println!("{}", s);
}
format!
最好的一点是它不会取得任何参数的所有权,这些参数在后续都可以继续使用。