标准库
基础
fn main() {
by_moving();
by_cloning();
by_mutating();
}
fn by_moving() {
let hello = "hello ".to_string();
let world = "world!";
// 将hello变量移动到一个新变量中
let hello_world = hello + world;
// hello变量不能再被使用
println!("{}", hello_world);
}
fn by_cloning() {
let hello = "hello ".to_string();
let world = "world!";
// 创建 hello 的副本并将其移动到新变量中
let hello_world = hello.clone() + world;
// hello变量仍可被使用
println!("{}", hello_world);
}
fn by_mutating() {
let mut hello = "hello ".to_string();
let world = "world!";
// hello变量被修改了
hello.push_str(world);
// hello 既可用又可修改
println!("{}", hello);
}
在所有的函数中,我们首先为一个长度可变的字符串分配内存。通过创建一个字符串切片(&str)并在其上调用 to_string 函数来实现这一点。
在Rust中连接字符串的一种方法,如by_moving函数所示,是将上述分配的内存,连同一个额外的字符串切片,移入一个新的变量。用+运算符连接,非常简单明了。但这种方法是有缺点的,hello在第12行之后就不能使用了,因为它被移动了。
by_cloning看起来与第一个函数几乎相同,但它将分配的字符串克隆到一个临时对象中,在进程中分配新的内存,然后将其移动,使原始的 hello 保持不变并且仍然可以访问。当然,这样做的代价是在运行时进行多余的内存分配。
by_mutating是解决我们问题的有状态方法。它在原来的地方执行所涉及的内存管理,这意味着性能应该与 by_moving 中的相同。最后,它使hello可变,为进一步的改变做好准备。这个函数看起来不那么优雅,因为它没有使用 + 运算符。这是有意为之,因为 Rust 试图推动您通过其设计来移动数据,以便在不改变现有变量的情况下创建新变量。
fn main() {
let colour = "red";
// '{}'会被参数替换。
let favourite = format!("My favourite colour is {}", colour);
println!("{}", favourite);
// 可以添加多个参数
let hello = "hello ";
let world = "world!";
let hello_world = format!("{}{}", hello, world);
println!("{}", hello_world);
// format! 宏可以连接任何实现了'Display' trait的数据类型,例如数字
let favourite_num = format!("My favourite number is {}", 42);
println!("{}", favourite_num); // "My favourite number is 42
// 如果要在字符串中多次包含某些参数,可以使用位置索引参数
let duck_duck_goose = format!("{0}, {0}, {0}, {1}!", "duck", "goose");
println!("{}", duck_duck_goose); // "duck, duck, duck, goose!"
// 你还可以给参数命名
let introduction = format!(
"My name is {surname}, {forename} {surname}",
surname = "Bond",
forename = "James"
);
println!("{}", introduction) // "My name is Bond, James Bond"
}
集合
Vector
fn main() {
// 创建一个vector
let fruits = vec!["apple", "tomato", "pear"];
// 不能直接打印向量,但我们可以debug-print
println!("fruits: {:?}", fruits);
// 创建一个空 vector 并填充它
let mut fruits = Vec::new();
fruits.push("apple");
fruits.push("tomato");
fruits.push("pear");
println!("fruits: {:?}", fruits);
// 删除最后一个元素
let last = fruits.pop();
if let Some(last) = last {
println!("Removed {} from {:?}", last, fruits);
}
// 在指定索引处插入一个元素
fruits.insert(1, "grape");
println!("fruits after insertion: {:?}", fruits);
// 交换两个元素
fruits.swap(0, 1);
println!("fruits after swap: {:?}", fruits);
// 访问第一个和最后一个元素
let first = fruits.first();
if let Some(first) = first {
println!("First fruit: {}", first);
}
let last = fruits.last();
if let Some(last) = last {
println!("Last fruit: {}", last);
}
// 访问任意元素
let second = fruits.get(1);
if let Some(second) = second {
println!("Second fruit: {}", second);
}
// 访问任意元素,不需要检查返回值
let second = fruits[1];
println!("Second fruit: {}", second);
// 用一个值初始化向量
// 这里,用五个零填充向量
let bunch_of_zeroes = vec![0; 5];
println!("bunch_of_zeroes: {:?}", bunch_of_zeroes);
// 删除指定索引处的元素
let mut nums = vec![1, 2, 3, 4];
let second_num = nums.remove(1);
println!("Removed {} from {:?}", second_num, nums);
// 过滤 vector
let mut names = vec!["Aaron", "Felicia", "Alex", "Daniel"];
// 只保留以"A"开头的名字
names.retain(|name| name.starts_with('A'));
println!("Names starting with A: {:?}", names);
// 检查vector是否包含元素
println!("Does 'names' contain \"Alex\"? {}", names.contains(&"Alex"));
// 删除连续的重复内容
let mut nums = vec![1, 2, 2, 3, 4, 4, 4, 5];
nums.dedup();
println!("Deduped, pre-sorted nums: {:?}", nums);
// 如果数据未排序,应小心使用
let mut nums = vec![2, 1, 4, 2, 3, 5, 1, 2];
nums.dedup();
// 可能打印出来的不是你所期望的
println!("Deduped, unsorted nums: {:?}", nums);
// 进行排序
nums.sort();
println!("Manually sorted nums: {:?}", nums);
nums.dedup();
println!("Deduped, sorted nums: {:?}", nums);
// 反转
nums.reverse();
println!("nums after being reversed: {:?}", nums);
// 在一个区间上创建消费迭代器
let mut alphabet = vec!['a', 'b', 'c'];
print!("The first two letters of the alphabet are: ");
for letter in alphabet.drain(..2) {
print!("{} ", letter);
}
println!();
// 被抽出的元素不再在vector中
println!("alphabet after being drained: {:?}", alphabet);
// 检查vector是否为空
let mut fridge = vec!["Beer", "Leftovers", "Mayonaise"];
println!("Is the fridge empty {}", fridge.is_empty());
// 删除所有元素
fridge.clear();
println!("Is the fridge now empty? {}", fridge.is_empty());
// 将一个vector分成两部分
let mut colors = vec!["red", "green", "blue", "yellow"];
println!("colors before splitting: {:?}", colors);
let half = colors.len() / 2;
let mut second_half = colors.split_off(half);
println!("colors after splitting: {:?}", colors);
println!("second_half: {:?}", second_half);
// 将两个vector放到一起
colors.append(&mut second_half);
println!("colors after appending: {:?}", colors);
// 这将清空第二个vector
println!("second_half after appending: {:?}", second_half);
// 拼接一个vector
let mut stuff = vec!["1", "2", "3", "4", "5", "6"];
println!("Original stuff: {:?}", stuff);
let stuff_to_insert = vec!["a", "b", "c"];
let removed_stuff: Vec<_> = stuff.splice(1..4, stuff_to_insert).collect();
println!("Spliced stuff: {:?}", stuff);
println!("Removed stuff: {:?}", removed_stuff);
// 优化:初始化具有指定容量的vector
let mut large_vec: Vec<i32> = Vec::with_capacity(1_000_000);
println!("large_vec after creation:");
println!("len:\t\t{}", large_vec.len());
println!("capacity:\t{}", large_vec.capacity());
// 尽可能地将vector缩小到接近其长度
large_vec.shrink_to_fit();
println!("large_vec after shrinking:");
println!("len:\t\t{}", large_vec.len());
println!("capacity:\t{}", large_vec.capacity());
// 删除指定项,用最后一个替换它
let mut nums = vec![1, 2, 3, 4];
let second_num = nums.swap_remove(1);
// 这会改变顺序,性能为O(1)
println!("Removed {} from {:?}", second_num, nums);
}
vector应该始终是首选集合。在内部,它被实现为一个存储在堆上的连续的内存块。
缩短vector时,额外的容量并没有消失。如果你有一个长度为10,000、容量为100,000的vector,并对其调用clear,你仍然会有100,000的预分配容量。当在有内存限制的系统上工作时,如微控制器,这可能成为一个问题。解决方案是定期对这些向量调shrink_to_fit。这将使容量尽可能地接近长度,但允许仍然留下一点预分配的空间。
另一种优化方法是调用swap_remove。通常,当从vector中移除一个元素时,后面的所有元素将向左移动,以保留连续的内存。当移除一个很大的vector中的第一个元素时,这是一个很耗时的工作。如果你不关心vector的顺序,则可以调用swap_remove而不是remove。它的工作原理是将要删除的元素与最后一个元素进行交换,并调整长度。这样就不会产生内存移动,仅交换内存是一个非常快的操作。
字符串
String 只是一种字符vector
fn main() {
// 由于 String 是一种vector,你可以用相同的方式构造它
let mut s = String::new();
s.push('H');
s.push('i');
println!("s: {}", s);
// 然而,String也可以从字符串切片 (&str) 构造
// 接下来的两种方法是等价的
let s = "Hello".to_string();
println!("s: {}", s);
let s = String::from("Hello");
println!("s: {}", s);
// Rust 中的字符串总是有效的 UTF-8
let s = "汉语 한글 Þjóðhildur 😉 🍺".to_string();
println!("s: {}", s);
// 将字符串相互附加
let mut s = "Hello ".to_string();
s.push_str("World");
// 遍历字符
// 此处将"character"定义为 Unicode 标量值
for ch in "Tubular".chars() {
print!("{}.", ch);
}
println!();
// 但是要小心,"character"可能并不总是你所期望的
for ch in "y̆".chars() {
// 这不会打印 y̆
print!("{} ", ch);
}
println!();
// 以各种方式拆分字符串
// 将一个字符串切片分成两半
let (first, second) = "HelloThere".split_at(5);
println!("first: {}, second: {}", first, second);
// 在单独的行上拆分
let haiku = "\
she watches\n\
satisfied after love\n\
he lies\n\
looking up at nothing\n\
";
for line in haiku.lines() {
println!("\t{}.", line);
}
// 拆分子串
for s in "Never;Give;Up".split(';') {
println!("{}", s);
}
// 当分割字符串在开头或结尾时,将导致空字符串
let s: Vec<_> = "::Hi::There::".split("::").collect();
println!("{:?}", s);
// 可以使用 split_termitor 消除末尾的空字符串
let s: Vec<_> = "Mr. T.".split_terminator('.').collect();
println!("{:?}", s);
// char 有一些方法可以用来拆分
for s in "I'm2fast4you".split(char::is_numeric) {
println!("{}", s);
}
// 只拆分一定的次数
for s in "It's not your fault, it's mine".splitn(3, char::is_whitespace) {
println!("{}", s);
}
// 只获取与模式匹配的子字符串,这与拆分相反
for c in "The Dark Knight rises".matches(char::is_uppercase) {
println!("{}", c);
}
// 检查字符串是否以某某开头
let saying = "The early bird gets the worm";
let starts_with_the = saying.starts_with("The");
println!("Does \"{}\" start with \"The\"?: {}", saying, starts_with_the);
let starts_with_bird = saying.starts_with("bird");
println!("Does \"{}\" start with \"bird\"?: {}", saying, starts_with_bird);
// 检查字符串是否以某某结尾
let ends_with_worm = saying.ends_with("worm");
println!("Does \"{}\" end with \"worm\"?: {}", saying, ends_with_worm);
// 检查字符串是否包含某些内容
let contains_bird = saying.contains("bird");
println!("Does \"{}\" contain \"bird\"?: {}", saying, contains_bird);
// 删除空格
// 在空白处拆分可能不会产生期望的结果
let a_lot_of_whitespace = " I love spaaace ";
let s: Vec<_> = a_lot_of_whitespace.split(' ').collect();
println!("{:?}", s);
// 改用 split_whitespace
let s: Vec<_> = a_lot_of_whitespace.split_whitespace().collect();
println!("{:?}", s);
// 删除前后空格
let username = " P3ngu1n\n".trim();
println!("{}", username);
// 仅删除前面空格
let username = " P3ngu1n\n".trim_left();
println!("{}", username);
// 仅删除尾部空格
let username = " P3ngu1n\n".trim_right();
println!("{}", username);
// 将字符串解析为另一种数据类型
// 这需要指定泛型
let num = "12".parse::<i32>();
if let Ok(num) = num {
println!("{} * {} = {}", num, num, num * num);
}
// 修改字符串
// 替换所有出现的模式
let s = "My dad is the best dad";
let new_s = s.replace("dad", "mom");
println!("new_s: {}", new_s);
// 用小写替换所有字符
let lowercase = s.to_lowercase();
println!("lowercase: {}", lowercase);
// 用大写替换所有字符
let uppercase = s.to_uppercase();
println!("uppercase: {}", uppercase);
// 这些也适用于其他语言
let greek = "ὈΔΥΣΣΕΎΣ";
println!("lowercase greek: {}", greek.to_lowercase());
// 重复一个字符串
let hello = "Hello! ";
println!("Three times hello: {}", hello.repeat(3));
}
迭代器访问集合
fn main() {
let names = vec!["Joe", "Miranda", "Alice"];
// 可以通过多种方式访问迭代器
// 几乎所有集合都实现了 .iter()
let mut iter = names.iter();
// 字符串本身是不可迭代的,但它的字符可以
let mut alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars();
// Range也是(有限的)迭代器
let nums = 0..10;
// 甚至可以创建无限迭代器
let all_nums = 0..;
// 你可以对迭代器进行迭代
// 这将消费迭代器
for num in nums {
print!("{} ", num);
}
// nums 不再可用
println!();
// 获取当前项的索引
for (index, letter) in "abc".chars().enumerate() {
println!("#{}. letter in the alphabet: {}", index + 1, letter);
}
// 通过迭代器,一步一步迭代
if let Some(name) = iter.next() {
println!("First name: {}", name);
}
if let Some(name) = iter.next() {
println!("Second name: {}", name);
}
if let Some(name) = iter.next() {
println!("Third name: {}", name);
}
if iter.next().is_none() {
println!("No names left");
}
// 任意访问迭代器中的项
let letter = alphabet.nth(3);
if let Some(letter) = letter {
println!("the fourth letter in the alphabet is: {}", letter);
}
// 这是通过消耗所有项直到指定位置
let current_first = alphabet.nth(0);
if let Some(current_first) = current_first {
// 不会打印“A”
println!(
"The first item in the iterator is currently: {}",
current_first
);
}
// 访问最后一项;将消耗整个迭代器
let last_letter = alphabet.last();
if let Some(last_letter) = last_letter {
println!("The last letter of the alphabet is: {}", last_letter);
}
// 将迭代器收集到集合中,这需要指定返回的集合类型
// 下面两个是等价的:
let nums: Vec<_> = (1..10).collect();
println!("nums: {:?}", nums);
let nums = (1..10).collect::<Vec<_>>();
println!("nums: {:?}", nums);
// 修改正在迭代的项
// 只取前 n 项
// 可以使无限迭代器有限
let nums: Vec<_> = all_nums.take(5).collect();
println!("The first five numbers are: {:?}", nums);
// 跳过前n项
let nums: Vec<_> = (0..11).skip(2).collect();
println!("The last 8 letters in a range from zero to 10: {:?}", nums);
// 使用take_while 和skip_while接受闭包
let nums: Vec<_> = (0..).take_while(|x| x * x < 50).collect();
println!(
"All positive numbers that are less than 50 when squared: {:?}",
nums
);
// 对于过滤已经排序的vector很有用
let names = ["Alfred", "Andy", "Jose", "Luke"];
let names: Vec<_> = names.iter().skip_while(|x| x.starts_with('A')).collect();
println!("Names that don't start with 'A': {:?}", names);
// 过滤迭代器
let countries = [
"U.S.A.", "Germany", "France", "Italy", "India", "Pakistan", "Burma"
];
let countries_with_i: Vec<_> = countries
.iter()
.filter(|country| country.contains('i'))
.collect();
println!(
"Countries containing the letter 'i': {:?}",
countries_with_i
);
// 检查迭代器是否包含元素
// 找到满足条件的第一个元素
if let Some(country) = countries.iter().find(|country| country.starts_with('I')) {
println!("First country starting with the letter 'I': {}", country);
}
// 不获取搜索的项,而是获取它的索引
if let Some(pos) = countries
.iter()
.position(|country| country.starts_with('I'))
{
println!("It's index is: {}", pos);
}
// 检查是否至少有一项满足条件
let are_any = countries.iter().any(|country| country.len() == 5);
println!(
"Is there at least one country that has exactly five letters? {}",
are_any
);
// 检查所有项是否满足条件
let are_all = countries.iter().all(|country| country.len() == 5);
println!("Do all countries have exactly five letters? {}", are_all);
// 数字项的有用操作
let sum: i32 = (1..11).sum();
let product: i32 = (1..11).product();
println!(
"When operating on the first ten positive numbers\n\
their sum is {} and\n\
their product is {}.",
sum, product
);
let max = (1..11).max();
let min = (1..11).min();
if let Some(max) = max {
println!("They have a highest number, and it is {}", max);
}
if let Some(min) = min {
println!("They have a smallest number, and it is {}", min);
}
// 组合迭代器
// 将迭代器与自身结合,使其无限
// 当它走到尽头时,它又重新开始
let some_numbers: Vec<_> = (1..4).cycle().take(10).collect();
println!("some_numbers: {:?}", some_numbers);
// 通过把两个迭代器放在另一个迭代器之后来合并它们
let some_numbers: Vec<_> = (1..4).chain(10..14).collect();
println!("some_numbers: {:?}", some_numbers);
// 将两个迭代器压缩在一起,它们的第一个项分组到一起,第二项也在一起,以此类推
let swiss_post_codes = [8957, 5000, 5034];
let swiss_towns = ["Spreitenbach", "Aarau", "Suhr"];
let zipped: Vec<_> = swiss_post_codes.iter().zip(swiss_towns.iter()).collect();
println!("zipped: {:?}", zipped);
// 因为 zip 是惰性的,所以你可以使用两个范围限制
let zipped: Vec<_> = (b'A'..)
.zip(1..)
.take(10)
.map(|(ch, num)| (ch as char, num))
.collect();
println!("zipped: {:?}", zipped);
// 将函数应用于所有项
// 更改项的类型
let numbers_as_strings: Vec<_> = (1..11).map(|x| x.to_string()).collect();
println!("numbers_as_strings: {:?}", numbers_as_strings);
// 遍历所有项
println!("First ten squares:");
(1..11).for_each(|x| print!("{} ", x));
println!();
// 同时过滤和映射项
let squares: Vec<_> = (1..50)
.filter_map(|x| if x % 3 == 0 { Some(x * x) } else { None })
.collect();
println!(
"Squares of all numbers under 50 that are divisible by 3: {:?}",
squares
);
// 迭代器的真正优势来自于它们的组合
// 检索整个字母表的小写和大写字母:
let alphabet: Vec<_> = (b'A' .. b'z' + 1) // 从 u8 开始
.map(|c| c as char) // 全部转换为字符
.filter(|c| c.is_alphabetic()) // 仅过滤字母字符
.collect(); // 收集为 Vec<char>
println!("alphabet: {:?}", alphabet);
}
iter()会创建一个借用元素项的迭代器。如果你想创建一个消耗元素项的迭代器,例如,通过移动它们来获得它们的所有权,你可以使用 into_iter()。
HashMap
use std::collections::HashMap;
fn main() {
// HashMap 可以将任何可散列类型映射到任何其他类型
// 第一种类型称为"key",第二种称为"value"
let mut tv_ratings = HashMap::new();
// 这里,我们将 &str 映射到 i32
tv_ratings.insert("The IT Crowd", 8);
tv_ratings.insert("13 Reasons Why", 7);
tv_ratings.insert("House of Cards", 9);
tv_ratings.insert("Stranger Things", 8);
tv_ratings.insert("Breaking Bad", 10);
// 检查key是否存在
let contains_tv_show = tv_ratings.contains_key("House of Cards");
println!("Did we rate House of Cards? {}", contains_tv_show);
let contains_tv_show = tv_ratings.contains_key("House");
println!("Did we rate House? {}", contains_tv_show);
// 访问value
if let Some(rating) = tv_ratings.get("Breaking Bad") {
println!("I rate Breaking Bad {} out of 10", rating);
}
// 给相同key插入不同的value,就会覆盖它
let old_rating = tv_ratings.insert("13 Reasons Why", 9);
if let Some(old_rating) = old_rating {
println!("13 Reasons Why's old rating was {} out of 10", old_rating);
}
if let Some(rating) = tv_ratings.get("13 Reasons Why") {
println!("But I changed my mind, it's now {} out of 10", rating);
}
// 删除一个key和它的value
let removed_value = tv_ratings.remove("The IT Crowd");
if let Some(removed_value) = removed_value {
println!("The removed series had a rating of {}", removed_value);
}
// 迭代访问所有键和值
println!("All ratings:");
for (key, value) in &tv_ratings {
println!("{}\t: {}", key, value);
}
// 还可以可变地迭代,迭代的时候修改
println!("All ratings with 100 as a maximum:");
for (key, value) in &mut tv_ratings {
*value *= 10;
println!("{}\t: {}", key, value);
}
// 在不引用 HashMap 的情况下进行迭代会移动其内容
for _ in tv_ratings {}
// tv_ratings 不再可用
// 与其他集合一样,可以预先分配容量大小以优化性能
let mut age = HashMap::with_capacity(10);
age.insert("Dory", 8);
age.insert("Nemo", 3);
age.insert("Merlin", 10);
age.insert("Bruce", 9);
// 迭代所有 key
println!("All names:");
for name in age.keys() {
println!("{}", name);
}
// 迭代所有 value
println!("All ages:");
for age in age.values() {
println!("{}", age);
}
// 迭代所有值并修改
println!("All ages in 10 years");
for age in age.values_mut() {
*age += 10;
println!("{}", age);
}
// 使用entry 为尚未在 HashMap 中的值分配默认值
{
let age_of_coral = age.entry("coral").or_insert(11);
println!("age_of_coral: {}", age_of_coral);
}
let age_of_coral = age.entry("coral").or_insert(15);
println!("age_of_coral: {}", age_of_coral);
}