pull/222/head
sunface 3 years ago
commit 4f748773fa

@ -4,4 +4,4 @@ Rust之所以能成为万众瞩目的语言就是因为其内存安全性。
理解所有权和借用对于Rust学习是至关重要的因此我们把本章提到了非常靠前的位置So骚年们准备好迎接狂风暴雨了嘛
从现在开始,鉴于大家已经掌握了非常基本的语法,有些时候,在示例代码中,将省略`fn main{}`的模版代码,只要将相应的示例放在`fn main{}`中,即可运行。
从现在开始,鉴于大家已经掌握了非常基本的语法,有些时候,在示例代码中,将省略`fn main() {}`的模版代码,只要将相应的示例放在`fn main() {}`中,即可运行。

@ -106,7 +106,7 @@ fn main() {
在终端运行上述代码时,会看到很多`debug: ...`的输出, 上面有讲,这些都是`条件编译`的输出, 那么该怎么消除掉这些输出呢?
读者大大普遍冰雪聪明,肯定已经想到:是的,在[认识Cargo](./cargo.md#手动编译和运行项目)中,曾经介绍过`--relese`参数,因为`cargo run`默认是运行`debug`模式. 因此想要消灭那些`debug:`输出,需要更改为其它模式,其中最常用的模式就是`--release`也就是生产发布的模式。
读者大大普遍冰雪聪明,肯定已经想到:是的,在[认识Cargo](./cargo.md#手动编译和运行项目)中,曾经介绍过`--release`参数,因为`cargo run`默认是运行`debug`模式. 因此想要消灭那些`debug:`输出,需要更改为其它模式,其中最常用的模式就是`--release`也就是生产发布的模式。
具体运行代码就不给了,留给大家作为一个小练习,建议亲自动手尝试下。

@ -1,13 +1,28 @@
# 冒泡排序
```rust
pub fn bubble_sort<T: Ord>(arr: &mut [T]) {
for i in 0..arr.len() {
for j in 0..arr.len() - 1 - i {
if arr[j] > arr[j + 1] {
arr.swap(j, j + 1);
pub fn bubble_sort<T: PartialOrd>(arr: &mut [T]) {
if arr.len() <= 1 {
return;
}
let size = arr.len();
for i in 0..(size - 1) {
// 标记当前循环是否发生元素交换
let mut swapped = false;
// 最后i个元素已经排好了顺序
for j in 1..(size - i) {
if arr[j - 1] > arr[j] {
arr.swap(j - 1, j);
swapped = true;
}
}
// 如果当前循环没有发生元素交换,说明数组已经有序
if !swapped {
break;
}
}
}
@ -16,23 +31,37 @@ mod tests {
use super::*;
#[test]
fn descending() {
//降序排列
let mut ve1 = vec![6, 5, 4, 3, 2, 1];
bubble_sort(&mut ve1);
for i in 0..ve1.len() - 1 {
assert!(ve1[i] <= ve1[i + 1]);
}
fn test_empty_vec() {
let mut empty_vec: Vec<String> = vec![];
bubble_sort(&mut empty_vec);
assert_eq!(empty_vec, Vec::<String>::new());
}
#[test]
fn ascending() {
//升序,预排序
let mut ve2 = vec![1, 2, 3, 4, 5, 6];
bubble_sort(&mut ve2);
for i in 0..ve2.len() - 1 {
assert!(ve2[i] <= ve2[i + 1]);
}
fn test_number_vec() {
let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9];
bubble_sort(&mut vec);
assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]);
}
#[test]
fn test_string_vec() {
let mut vec = vec![
String::from("Bob"),
String::from("David"),
String::from("Carol"),
String::from("Alice"),
];
bubble_sort(&mut vec);
assert_eq!(
vec,
vec![
String::from("Alice"),
String::from("Bob"),
String::from("Carol"),
String::from("David"),
]
);
}
}
```

@ -1,96 +1,38 @@
# 堆排序
```rust
/// Sort a mutable slice using heap sort.
///
/// Heap sort is an in-place O(n log n) sorting algorithm. It is based on a
/// max heap, a binary tree data structure whose main feature is that
/// parent nodes are always greater or equal to their child nodes.
///
/// # Max Heap Implementation
///
/// A max heap can be efficiently implemented with an array.
/// For example, the binary tree:
/// ```text
/// 1
/// 2 3
/// 4 5 6 7
/// ```
///
/// ... is represented by the following array:
/// ```text
/// 1 23 4567
/// ```
///
/// Given the index `i` of a node, parent and child indices can be calculated
/// as follows:
/// ```text
/// parent(i) = (i-1) / 2
/// left_child(i) = 2*i + 1
/// right_child(i) = 2*i + 2
/// ```
/// # Algorithm
///
/// Heap sort has two steps:
/// 1. Convert the input array to a max heap.
/// 2. Partition the array into heap part and sorted part. Initially the
/// heap consists of the whole array and the sorted part is empty:
/// ```text
/// arr: [ heap |]
/// ```
///
/// Repeatedly swap the root (i.e. the largest) element of the heap with
/// the last element of the heap and increase the sorted part by one:
/// ```text
/// arr: [ root ... last | sorted ]
/// --> [ last ... | root sorted ]
/// ```
///
/// After each swap, fix the heap to make it a valid max heap again.
/// Once the heap is empty, `arr` is completely sorted.
pub fn heap_sort<T: Ord>(arr: &mut [T]) {
if arr.len() <= 1 {
return; // already sorted
pub fn heap_sort<T: PartialOrd>(arr: &mut [T]) {
let size = arr.len();
// 构建大根堆
for i in (0..size / 2).rev() {
heapify(arr, i, size);
}
heapify(arr);
for end in (1..arr.len()).rev() {
arr.swap(0, end);
move_down(&mut arr[..end], 0);
// 每轮循环将堆顶元素(也就是最大元素)放到最后
for i in (1..size).rev() {
arr.swap(0, i);
// 恢复大根堆
heapify(arr, 0, i);
}
}
/// Convert `arr` into a max heap.
fn heapify<T: Ord>(arr: &mut [T]) {
let last_parent = (arr.len() - 2) / 2;
for i in (0..=last_parent).rev() {
move_down(arr, i);
fn heapify<T: PartialOrd>(arr: &mut [T], root: usize, end: usize) {
// 记录父节点和左右节点中最大元素的索引位置
let mut largest = root;
let left_child = 2 * root + 1;
if left_child < end && arr[left_child] > arr[largest] {
largest = left_child;
}
}
/// Move the element at `root` down until `arr` is a max heap again.
///
/// This assumes that the subtrees under `root` are valid max heaps already.
fn move_down<T: Ord>(arr: &mut [T], mut root: usize) {
let last = arr.len() - 1;
loop {
let left = 2 * root + 1;
if left > last {
break;
}
let right = left + 1;
let max = if right <= last && arr[right] > arr[left] {
right
} else {
left
};
let right_child = left_child + 1;
if right_child < end && arr[right_child] > arr[largest] {
largest = right_child;
}
if arr[max] > arr[root] {
arr.swap(root, max);
}
root = max;
if largest != root {
arr.swap(root, largest);
heapify(arr, largest, end);
}
}
@ -99,45 +41,37 @@ mod tests {
use super::*;
#[test]
fn empty() {
let mut arr: Vec<i32> = Vec::new();
heap_sort(&mut arr);
assert_eq!(&arr, &[]);
}
#[test]
fn single_element() {
let mut arr = vec![1];
heap_sort(&mut arr);
assert_eq!(&arr, &[1]);
}
#[test]
fn sorted_array() {
let mut arr = vec![1, 2, 3, 4];
heap_sort(&mut arr);
assert_eq!(&arr, &[1, 2, 3, 4]);
}
#[test]
fn unsorted_array() {
let mut arr = vec![3, 4, 2, 1];
heap_sort(&mut arr);
assert_eq!(&arr, &[1, 2, 3, 4]);
fn test_empty_vec() {
let mut empty_vec: Vec<String> = vec![];
heap_sort(&mut empty_vec);
assert_eq!(empty_vec, Vec::<String>::new());
}
#[test]
fn odd_number_of_elements() {
let mut arr = vec![3, 4, 2, 1, 7];
heap_sort(&mut arr);
assert_eq!(&arr, &[1, 2, 3, 4, 7]);
fn test_number_vec() {
let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9];
heap_sort(&mut vec);
assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]);
}
#[test]
fn repeated_elements() {
let mut arr = vec![542, 542, 542, 542];
heap_sort(&mut arr);
assert_eq!(&arr, &vec![542, 542, 542, 542]);
fn test_string_vec() {
let mut vec = vec![
String::from("Bob"),
String::from("David"),
String::from("Carol"),
String::from("Alice"),
];
heap_sort(&mut vec);
assert_eq!(
vec,
vec![
String::from("Alice"),
String::from("Bob"),
String::from("Carol"),
String::from("David"),
]
);
}
}
```

@ -1,25 +1,27 @@
# 插入排序
```rust
use std::cmp;
/// Sorts a mutable slice using in-place insertion sort algorithm.
///
/// Time complexity is `O(n^2)`, where `n` is the number of elements.
/// Space complexity is `O(1)` as it sorts elements in-place.
pub fn insertion_sort<T>(arr: &mut [T])
where
T: cmp::PartialOrd + Copy,
{
pub fn insertion_sort<T: PartialOrd>(arr: &mut [T]) {
// 从第二个元素开始排序
for i in 1..arr.len() {
let cur = arr[i];
let mut j = i - 1;
// 找到 arr[i] 该插入的位置
let mut j = i;
while j > 0 && arr[j - 1] > arr[j] {
arr.swap(j - 1, j);
j -= 1;
}
}
}
while arr[j] > cur {
arr.swap(j + 1, j);
if j == 0 {
break;
}
// 这里需要 T: Ord 是因为 binary_search() 方法的限制
pub fn insertion_sort_binary_search<T: Ord>(arr: &mut[T]) {
// 从第二个元素开始排序
for i in 1..arr.len() {
// 利用二分查找获取 arr[i] 应该插入的位置
let pos = arr[..i].binary_search(&arr[i]).unwrap_or_else(|pos| pos);
let mut j = i;
while j > pos {
arr.swap(j - 1, j);
j -= 1;
}
}
@ -27,49 +29,82 @@ where
#[cfg(test)]
mod tests {
use super::super::is_sorted;
use super::*;
#[test]
fn empty() {
let mut arr: [u8; 0] = [];
insertion_sort(&mut arr);
assert!(is_sorted(&arr));
}
mod insertion_sort {
use super::*;
#[test]
fn one_element() {
let mut arr: [char; 1] = ['a'];
insertion_sort(&mut arr);
assert!(is_sorted(&arr));
}
#[test]
fn already_sorted() {
let mut arr: [&str; 3] = ["a", "b", "c"];
insertion_sort(&mut arr);
assert!(is_sorted(&arr));
}
#[test]
fn basic() {
let mut arr: [&str; 4] = ["d", "a", "c", "b"];
insertion_sort(&mut arr);
assert!(is_sorted(&arr));
}
#[test]
fn odd_number_of_elements() {
let mut arr: Vec<&str> = vec!["d", "a", "c", "e", "b"];
insertion_sort(&mut arr);
assert!(is_sorted(&arr));
#[test]
fn test_empty_vec() {
let mut empty_vec: Vec<String> = vec![];
insertion_sort(&mut empty_vec);
assert_eq!(empty_vec, Vec::<String>::new());
}
#[test]
fn test_number_vec() {
let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9];
insertion_sort(&mut vec);
assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]);
}
#[test]
fn test_string_vec() {
let mut vec = vec![
String::from("Bob"),
String::from("David"),
String::from("Carol"),
String::from("Alice"),
];
insertion_sort(&mut vec);
assert_eq!(
vec,
vec![
String::from("Alice"),
String::from("Bob"),
String::from("Carol"),
String::from("David"),
]
);
}
}
#[test]
fn repeated_elements() {
let mut arr: Vec<usize> = vec![542, 542, 542, 542];
insertion_sort(&mut arr);
assert!(is_sorted(&arr));
mod insertion_sort_binary_search {
use super::*;
#[test]
fn test_empty_vec() {
let mut empty_vec: Vec<String> = vec![];
insertion_sort_binary_search(&mut empty_vec);
assert_eq!(empty_vec, Vec::<String>::new());
}
#[test]
fn test_number_vec() {
let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9];
insertion_sort_binary_search(&mut vec);
assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]);
}
#[test]
fn test_string_vec() {
let mut vec = vec![
String::from("Bob"),
String::from("David"),
String::from("Carol"),
String::from("Alice"),
];
insertion_sort_binary_search(&mut vec);
assert_eq!(
vec,
vec![
String::from("Alice"),
String::from("Bob"),
String::from("Carol"),
String::from("David"),
]
);
}
}
}
```

@ -1,64 +1,56 @@
# 归并排序
```rust
fn _merge<T: Ord + Copy>(arr: &mut [T], lo: usize, mid: usize, hi: usize) {
// create temporary arrays to support merge
let mut left_half = Vec::new();
let mut right_half = Vec::new();
for v in arr.iter().take(mid + 1).skip(lo) {
left_half.push(*v);
}
for v in arr.iter().take(hi + 1).skip(mid + 1) {
right_half.push(*v);
pub fn merge_sort<T>(arr: &mut [T])
where
T: PartialOrd + Clone + Default,
{
if arr.len() > 1 {
merge_sort_range(arr, 0, arr.len() - 1);
}
}
let lsize = left_half.len();
let rsize = right_half.len();
fn merge_sort_range<T>(arr: &mut [T], lo: usize, hi: usize)
where
T: PartialOrd + Clone + Default,
{
// 只有当元素个数大于一时才进行排序
if lo < hi {
let mid = lo + ((hi - lo) >> 1);
merge_sort_range(arr, lo, mid);
merge_sort_range(arr, mid + 1, hi);
merge_two_arrays(arr, lo, mid, hi);
}
}
// pointers to track the positions while merging
let mut l = 0;
let mut r = 0;
let mut a = lo;
// 合并两个有序数组: arr[lo..=mid], arr[mid + 1..=hi]
fn merge_two_arrays<T>(arr: &mut [T], lo: usize, mid: usize, hi: usize)
where
T: PartialOrd + Clone + Default,
{
// 这里需要 clone 数组元素
let mut arr1 = arr[lo..=mid].to_vec();
let mut arr2 = arr[mid + 1..=hi].to_vec();
// pick smaller element one by one from either left or right half
while l < lsize && r < rsize {
if left_half[l] < right_half[r] {
arr[a] = left_half[l];
l += 1;
let (mut i, mut j) = (0, 0);
while i < arr1.len() && j < arr2.len() {
if arr1[i] < arr2[j] {
arr[i + j + lo] = std::mem::take(&mut arr1[i]);
i += 1;
} else {
arr[a] = right_half[r];
r += 1;
arr[i + j + lo] = std::mem::take(&mut arr2[j]);
j += 1;
}
a += 1;
}
// put all the remaining ones
while l < lsize {
arr[a] = left_half[l];
l += 1;
a += 1;
while i < arr1.len() {
arr[i + j + lo] = std::mem::take(&mut arr1[i]);
i += 1;
}
while r < rsize {
arr[a] = right_half[r];
r += 1;
a += 1;
}
}
fn _merge_sort<T: Ord + Copy>(arr: &mut [T], lo: usize, hi: usize) {
if lo < hi {
let mid = lo + (hi - lo) / 2;
_merge_sort(arr, lo, mid);
_merge_sort(arr, mid + 1, hi);
_merge(arr, lo, mid, hi);
}
}
pub fn merge_sort<T: Ord + Copy>(arr: &mut [T]) {
let len = arr.len();
if len > 1 {
_merge_sort(arr, 0, len - 1);
while j < arr2.len() {
arr[i + j + lo] = std::mem::take(&mut arr2[j]);
j += 1;
}
}
@ -67,45 +59,37 @@ mod tests {
use super::*;
#[test]
fn basic() {
let mut res = vec![10, 8, 4, 3, 1, 9, 2, 7, 5, 6];
merge_sort(&mut res);
assert_eq!(res, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
}
#[test]
fn basic_string() {
let mut res = vec!["a", "bb", "d", "cc"];
merge_sort(&mut res);
assert_eq!(res, vec!["a", "bb", "cc", "d"]);
}
#[test]
fn empty() {
let mut res = Vec::<u8>::new();
merge_sort(&mut res);
assert_eq!(res, vec![]);
}
#[test]
fn one_element() {
let mut res = vec![1];
merge_sort(&mut res);
assert_eq!(res, vec![1]);
fn test_empty_vec() {
let mut empty_vec: Vec<String> = vec![];
merge_sort(&mut empty_vec);
assert_eq!(empty_vec, Vec::<String>::new());
}
#[test]
fn pre_sorted() {
let mut res = vec![1, 2, 3, 4];
merge_sort(&mut res);
assert_eq!(res, vec![1, 2, 3, 4]);
fn test_number_vec() {
let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9];
merge_sort(&mut vec);
assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]);
}
#[test]
fn reverse_sorted() {
let mut res = vec![4, 3, 2, 1];
merge_sort(&mut res);
assert_eq!(res, vec![1, 2, 3, 4]);
fn test_string_vec() {
let mut vec = vec![
String::from("Bob"),
String::from("David"),
String::from("Carol"),
String::from("Alice"),
];
merge_sort(&mut vec);
assert_eq!(
vec,
vec![
String::from("Alice"),
String::from("Bob"),
String::from("Carol"),
String::from("David"),
]
);
}
}
```

@ -1,40 +1,125 @@
# 快速排序
```rust
use std::cmp::PartialOrd;
pub fn quick_sort<T: PartialOrd>(arr: &mut [T]) {
if arr.len() > 1 {
quick_sort_range(arr, 0, arr.len() - 1);
}
}
fn quick_sort_range<T: PartialOrd>(arr: &mut [T], lo: usize, hi: usize) {
// 只有当元素个数大于一时才进行排序
if lo < hi {
let pos = partition(arr, lo, hi);
// let pos = partition_random(arr, lo, hi);
if pos != 0 {
// 如果 pos == 0, pos - 1 会发生溢出错误
quick_sort_range(arr, lo, pos - 1);
}
quick_sort_range(arr, pos + 1, hi);
}
}
pub fn partition<T: PartialOrd>(arr: &mut [T], lo: isize, hi: isize) -> isize {
let pivot = hi as usize;
let mut i = lo - 1;
let mut j = hi;
fn partition<T: PartialOrd>(arr: &mut [T], lo: usize, hi: usize) -> usize {
// 默认选取 lo 作为 pivot
let pivot = lo;
loop {
i += 1;
while arr[i as usize] < arr[pivot] {
i += 1;
let (mut left, mut right) = (lo, hi);
while left < right {
// 找到右边第一个不大于等于 arr[pivot] 的元素
while left < right && arr[right] >= arr[pivot] {
right -= 1;
}
j -= 1;
while j >= 0 && arr[j as usize] > arr[pivot] {
j -= 1;
// 找到左边第一个不小于等于 arr[pivot] 的元素
while left < right && arr[left] <= arr[pivot] {
left += 1;
}
if i >= j {
break;
} else {
arr.swap(i as usize, j as usize);
// 交换前面找到的两个元素
if left != right {
arr.swap(left, right);
}
}
arr.swap(i as usize, pivot as usize);
i
arr.swap(pivot, left);
// 返回正确的分割位置
left
}
fn _quick_sort<T: Ord>(arr: &mut [T], lo: isize, hi: isize) {
if lo < hi {
let p = partition(arr, lo, hi);
_quick_sort(arr, lo, p - 1);
_quick_sort(arr, p + 1, hi);
// 随机选取 pivot 的位置
fn partition_random<T: PartialOrd>(arr: &mut [T], lo: usize, hi: usize) -> usize {
// 在 Cargo.toml 的依赖中添加 rand 库
use rand::Rng;
let mut rng = rand::thread_rng();
let pivot = rng.gen_range(lo..=hi);
// 交换 lo 和 pivot 位置上的元素,从而间接使得 pivot = lo
// 因此后序操作和 partition() 函数一致
arr.swap(lo, pivot);
let pivot = lo;
let (mut left, mut right) = (lo, hi);
while left < right {
// 找到右边第一个不大于等于 arr[pivot] 的元素
while left < right && arr[right] >= arr[pivot] {
right -= 1;
}
// 找到左边第一个不小于等于 arr[pivot] 的元素
while left < right && arr[left] <= arr[pivot] {
left += 1;
}
// 交换前面找到的两个元素
if left != right {
arr.swap(left, right);
}
}
arr.swap(pivot, left);
// 返回正确的分割位置
left
}
pub fn quick_sort<T: Ord>(arr: &mut [T]) {
let len = arr.len();
_quick_sort(arr, 0, (len - 1) as isize);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_vec() {
let mut empty_vec: Vec<String> = vec![];
quick_sort(&mut empty_vec);
assert_eq!(empty_vec, Vec::<String>::new());
}
#[test]
fn test_number_vec() {
let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9];
quick_sort(&mut vec);
assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]);
}
#[test]
fn test_string_vec() {
let mut vec = vec![
String::from("Bob"),
String::from("David"),
String::from("Carol"),
String::from("Alice"),
];
quick_sort(&mut vec);
assert_eq!(
vec,
vec![
String::from("Alice"),
String::from("Bob"),
String::from("Carol"),
String::from("David"),
]
);
}
}
```

@ -1,16 +1,24 @@
# 选择排序
```rust
pub fn selection_sort<T: Ord>(arr: &mut [T]) {
let len = arr.len();
for left in 0..len {
let mut smallest = left;
for right in (left + 1)..len {
if arr[right] < arr[smallest] {
smallest = right;
pub fn selection_sort<T: PartialOrd>(arr: &mut [T]) {
if arr.len() <= 1 {
return;
}
let size = arr.len();
for i in 0..(size - 1) {
// 找到最小元素的索引值
let mut min_index = i;
for j in (i + 1)..size {
if arr[j] < arr[min_index] {
min_index = j;
}
}
arr.swap(smallest, left);
if min_index != i {
arr.swap(i, min_index);
}
}
}
@ -19,31 +27,37 @@ mod tests {
use super::*;
#[test]
fn basic() {
let mut res = vec!["d", "a", "c", "b"];
selection_sort(&mut res);
assert_eq!(res, vec!["a", "b", "c", "d"]);
}
#[test]
fn empty() {
let mut res = Vec::<u8>::new();
selection_sort(&mut res);
assert_eq!(res, vec![]);
fn test_empty_vec() {
let mut empty_vec: Vec<String> = vec![];
selection_sort(&mut empty_vec);
assert_eq!(empty_vec, Vec::<String>::new());
}
#[test]
fn one_element() {
let mut res = vec!["a"];
selection_sort(&mut res);
assert_eq!(res, vec!["a"]);
fn test_number_vec() {
let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9];
selection_sort(&mut vec);
assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]);
}
#[test]
fn pre_sorted() {
let mut res = vec!["a", "b", "c"];
selection_sort(&mut res);
assert_eq!(res, vec!["a", "b", "c"]);
fn test_string_vec() {
let mut vec = vec![
String::from("Bob"),
String::from("David"),
String::from("Carol"),
String::from("Alice"),
];
selection_sort(&mut vec);
assert_eq!(
vec,
vec![
String::from("Alice"),
String::from("Bob"),
String::from("Carol"),
String::from("David"),
]
);
}
}
```

@ -1,9 +1,10 @@
# Error handling
Most errors arent serious enough to require the program to stop entirely.
Sometimes, when a function fails, its for a reason that you can easily interpret and respond to.
For example, if you try to open a file and that operation fails because the file doesnt exist, you might want to create the file instead of terminating the process.
# 错误处理
大多数的错误并没有严重到需要让程序完全停止运行的程度。
有时一个函数执行失败时,你可以很容易地对造成失败的原因进行解释并采取对应措施的。
例如,你正试图打开一个文件,但由于该文件不存在导致了操作失败,这时你可能想创建
该文件而不是直接终止程序。
## Further information
## 更多信息
- [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)
- [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html)

@ -1,18 +1,16 @@
// errors1.rs
// This function refuses to generate text to be printed on a nametag if
// you pass it an empty string. It'd be nicer if it explained what the problem
// was, instead of just sometimes returning `None`. The 2nd test currently
// does not compile or pass, but it illustrates the behavior we would like
// this function to have.
// Execute `rustlings hint errors1` for hints!
// 假使你传给这个函数一个空字符串那么它将拒绝生成一段个性签名nametage
// 如果它能解释拒绝的原因是什么,而不是粗暴返回 `None` 那就更完美了。
// 第 2 个测试目前还没通过并未能编译,但它说明了我们希望这个函数具有的行为。
// 执行 `rustlings hint errors1` 获取提示!
// I AM NOT DONE
pub fn generate_nametag_text(name: String) -> Option<String> {
pub fn generate_nametag_text(name: String) -> Option<String> {// 译:生成个性签名
if name.len() > 0 {
Some(format!("Hi! My name is {}", name))
Some(format!("Hi! My name is {}", name))// 译:"嗨!我的名字是 {}"
} else {
// Empty names aren't allowed.
// 不允许使用空的名字。
None
}
}
@ -21,11 +19,10 @@ pub fn generate_nametag_text(name: String) -> Option<String> {
mod tests {
use super::*;
// This test passes initially if you comment out the 2nd test.
// You'll need to update what this test expects when you change
// the function under test!
// 你可以注释掉第 2 个测试,那么这个测试就能初步通过。
// 当你更改了测试的函数时,也需要修改下测试代码以使测试正确!
#[test]
fn generates_nametag_text_for_a_nonempty_name() {
fn generates_nametag_text_for_a_nonempty_name() {// 译:用一个非空名称生成一段个性签名
assert_eq!(
generate_nametag_text("Beyoncé".into()),
Some("Hi! My name is Beyoncé".into())
@ -33,7 +30,7 @@ mod tests {
}
#[test]
fn explains_why_generating_nametag_text_fails() {
fn explains_why_generating_nametag_text_fails() {// 译:说明为什么个性签名生成失败了
assert_eq!(
generate_nametag_text("".into()),
Err("`name` was empty; it must be nonempty.".into())

@ -1,20 +1,16 @@
// errors2.rs
// Say we're writing a game where you can buy items with tokens. All items cost
// 5 tokens, and whenever you purchase items there is a processing fee of 1
// token. A player of the game will type in how many items they want to buy,
// and the `total_cost` function will calculate the total number of tokens.
// Since the player typed in the quantity, though, we get it as a string-- and
// they might have typed anything, not just numbers!
// Right now, this function isn't handling the error case at all (and isn't
// handling the success case properly either). What we want to do is:
// if we call the `parse` function on a string that is not a number, that
// function will return a `ParseIntError`, and in that case, we want to
// immediately return that error from our function and not try to multiply
// and add.
// There are at least two ways to implement this that are both correct-- but
// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways.
// 假设我们正在编写一个游戏,你可以用代币购买物品。
// 所有物品的价格都是 5 个代币,每当你购买物品时,都需要 1 个代币的小费。
// 游戏玩家将输入他们想要购买的物品数量,`total_cost` 函数能够计算出所需的代币数量。
// 虽然玩家输入的是数量,但我们得到的却是一个字符串——他们可能输入了任何东西,而不仅仅是数字!
// 目前这个函数没有处理任何错误的情况(也没有处理成功的情况)。
// 我们要做的是:
// 如果我们在非数字的字符串上调用 `parse` 方法,该方法将返回 `ParseIntError`
// 在这种情况下,我们要立刻从函数返回这个错误,而不是继续进行相关计算。
// 至少有两种方法可以做到这点,它们都是正确的——但其中一种简短得多!
// 执行 `rustlings hint errors2` 以获得关于这两种方式的提示。
// I AM NOT DONE
@ -42,6 +38,6 @@ mod tests {
assert_eq!(
total_cost("beep boop").unwrap_err().to_string(),
"invalid digit found in string"
);
);// 译:字符串中包含无效的数字
}
}

@ -1,8 +1,7 @@
// errors3.rs
// This is a program that is trying to use a completed version of the
// `total_cost` function from the previous exercise. It's not working though!
// Why not? What should we do to fix it?
// Execute `rustlings hint errors3` for hints!
// 这是一个试图使用前面练习中 `total_cost` 函数完整版的程序。
// 但出了些问题!为什么不行?我们需要怎样做才能解决问题?
// 执行 `rustlings hint errors3` 获取提示!
// I AM NOT DONE
@ -15,10 +14,10 @@ fn main() {
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
println!("You can't afford that many!");// 译:你的代币不足以完成支付!
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
println!("You now have {} tokens.", tokens);// 译:现在你有 {} 个代币"
}
}

@ -1,5 +1,5 @@
// errors4.rs
// Make this test pass! Execute `rustlings hint errors4` for hints :)
// 通过测试!执行 `rustlings hint errors4` 获取提示 :)
// I AM NOT DONE

@ -1,8 +1,8 @@
// errors5.rs
// This program uses a completed version of the code from errors4.
// It won't compile right now! Why?
// Execute `rustlings hint errors5` for hints!
// 这个程序使用练习 errors4 代码的完整版。
// 它现在不能编译! 为什么呢?
// 执行 `rustlings hint errors5` 获取提示!
// I AM NOT DONE
@ -10,7 +10,7 @@ use std::error;
use std::fmt;
use std::num::ParseIntError;
// TODO: update the return type of `main()` to make this compile.
// TODO:修改 `main()` 的返回类型,以使其通过编译。
fn main() -> Result<(), ParseIntError> {
let pretend_user_input = "42";
let x: i64 = pretend_user_input.parse()?;
@ -18,7 +18,7 @@ fn main() -> Result<(), ParseIntError> {
Ok(())
}
// Don't change anything below this line.
// 不要更改此行以下的任何内容。
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
@ -39,7 +39,7 @@ impl PositiveNonzeroInteger {
}
}
// This is required so that `CreationError` can implement `error::Error`.
// 以下是必要的,以便 `CreationError` 能够实现 `error::Error` 。
impl fmt::Display for CreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self {

@ -1,18 +1,17 @@
// errors6.rs
// Using catch-all error types like `Box<dyn error::Error>` isn't recommended
// for library code, where callers might want to make decisions based on the
// error content, instead of printing it out or propagating it further. Here,
// we define a custom error type to make it possible for callers to decide
// what to do next when our function returns an error.
// 像 `Box<dyn error::Error>` 这样的万能错误类型并不推荐用于库代码,
// 因为调用者可能想根据错误内容进行相关处理,而不是将其打印出来或进一步传播。
// 在这里,我们自定义了一个错误类型,让调用者有可能去决定当函数返回错误时
// 下一步该怎么做
// Make these tests pass! Execute `rustlings hint errors6` for hints :)
// 通过这些测试!执行 `rustlings hint errors6` 获取提示 :)
// I AM NOT DONE
use std::num::ParseIntError;
// This is a custom error type that we will be using in `parse_pos_nonzero()`.
// 这是一个自定义的错误类型,我们将在 `parse_pos_nonzero()` 中使用。
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
Creation(CreationError),
@ -23,20 +22,19 @@ impl ParsePosNonzeroError {
fn from_creation(err: CreationError) -> ParsePosNonzeroError {
ParsePosNonzeroError::Creation(err)
}
// TODO: add another error conversion function here.
// TODO:在这添加另一个错误转换函数。
}
fn parse_pos_nonzero(s: &str)
-> Result<PositiveNonzeroInteger, ParsePosNonzeroError>
{
// TODO: change this to return an appropriate error instead of panicking
// when `parse()` returns an error.
// TODO改为返回一个恰当的错误而不是在 `parse()` 返回一个错误时 panic
let x: i64 = s.parse().unwrap();
PositiveNonzeroInteger::new(x)
.map_err(ParsePosNonzeroError::from_creation)
}
// Don't change anything below this line.
// 不要更改这一行以下的任何内容。
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
@ -63,7 +61,7 @@ mod test {
#[test]
fn test_parse_error() {
// We can't construct a ParseIntError, so we have to pattern match.
// 我们不能构造一个 ParseIntError ,所以必须进行模式匹配。
assert!(matches!(
parse_pos_nonzero("not a number"),
Err(ParsePosNonzeroError::ParseInt(_))

@ -433,74 +433,63 @@ name = "errors1"
path = "exercises/error_handling/errors1.rs"
mode = "test"
hint = """
`Err` is one of the variants of `Result`, so what the 2nd test is saying
is that `generate_nametag_text` should return a `Result` instead of an
`Option`.
`Err` `Result` `generate_nametag_text`
`Result` `Option`
To make this change, you'll need to:
- update the return type in the function signature to be a Result<String, String> that
could be the variants `Ok(String)` and `Err(String)`
- change the body of the function to return `Ok(stuff)` where it currently
returns `Some(stuff)`
- change the body of the function to return `Err(error message)` where it
currently returns `None`
- change the first test to expect `Ok(stuff)` where it currently expects
`Some(stuff)`."""
- Result<String, String>便 `Ok(String)` `Err(String)`
- `Some(stuff)` `Ok(stuff)`
- `None` `Err(error message)`
- `Some(stuff)` `Ok(stuff)`"""
[[exercises]]
name = "errors2"
path = "exercises/error_handling/errors2.rs"
mode = "test"
hint = """
One way to handle this is using a `match` statement on
`item_quantity.parse::<i32>()` where the cases are `Ok(something)` and
`Err(something)`. This pattern is very common in Rust, though, so there's
a `?` operator that does pretty much what you would make that match statement
do for you! Take a look at this section of the Error Handling chapter:
`item_quantity.parse::<i32>()` 使 match
`Ok(something)` `Err(something)`
Rust `?`
Error Handling :
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
and give it a try!"""
"""
[[exercises]]
name = "errors3"
path = "exercises/error_handling/errors3.rs"
mode = "compile"
hint = """
If other functions can return a `Result`, why shouldn't `main`?"""
`Result` `main` """
[[exercises]]
name = "errors4"
path = "exercises/error_handling/errors4.rs"
mode = "test"
hint = """
`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result.
It should be doing some checking, returning an `Err` result if those checks fail, and only
returning an `Ok` result if those checks determine that everything is... okay :)"""
`PositiveNonzeroInteger::new` `Ok`
`Err` `Ok` :)"""
[[exercises]]
name = "errors5"
path = "exercises/error_handling/errors5.rs"
mode = "compile"
hint = """
Hint: There are two different possible `Result` types produced within
`main()`, which are propagated using `?` operators. How do we declare a
return type from `main()` that allows both?
`main()` `Result` `?`
`main()`
Another hint: under the hood, the `?` operator calls `From::from`
on the error value to convert it to a boxed trait object, a
`Box<dyn error::Error>`, which is polymorphic-- that means that lots of
different kinds of errors can be returned from the same function because
all errors act the same since they all implement the `error::Error` trait.
Check out this section of the book:
`?` `From::from`
`Box<dyn error::Error>`
`error::Error`
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
This exercise uses some concepts that we won't get to until later in the
course, like `Box` and the `From` trait. It's not important to understand
them in detail right now, but you can read ahead if you like.
使 `Box` `From`
Read more about boxing errors:
boxing errors
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
Read more about using the `?` operator with boxed errors:
使 `?`
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
"""
@ -509,19 +498,15 @@ name = "errors6"
path = "exercises/error_handling/errors6.rs"
mode = "test"
hint = """
This exercise uses a completed version of `PositiveNonzeroInteger` from
errors4.
使 error 4 `PositiveNonzeroInteger`
Below the line that TODO asks you to change, there is an example of using
the `map_err()` method on a `Result` to transform one type of error into
another. Try using something similar on the `Result` from `parse()`. You
might use the `?` operator to return early from the function, or you might
use a `match` expression, or maybe there's another way!
TODO `Result` 使 `map_err()`
`parse()` `Result` 使西
使 `?` 使 `match`
You can create another function inside `impl ParsePosNonzeroError` to use
with `map_err()`.
`impl ParsePosNonzeroError` `map_err()` 使
Read more about `map_err()` in the `std::result` documentation:
`std::result` `map_err()`
https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err"""
# Generics

Loading…
Cancel
Save