Introduction

Welcome to Rust Ocean.

Reader positioning

Position the reader to understand the basic programming concepts. In order to briefly ignore the basic concepts, if you feel that understanding is difficult, consider looking for a more basic book.

What is Rust?

Rust is a modern system programming language that focuses on safety, speed, and concurrency. Rust achieves these goals through memory security, but does not use the Garbage Collection (GC).

Rust solves the null pointer problem, and the error handling is more reliable, greatly increasing reliability and confidence in the code.

If you have read the brief introduction above me and still don’t know the truth, if you are still hesitating to learn Rust, you can browse “Love Rust”. You can check out the short video introduction of intorust before reading.

Minimalist style

This book adopts a minimalist style and does not seek encyclopedia. The goal is to have a basic understanding after the completion of the course. Hope is an easy way to finish in two or three days.

Refer to Study Rust by Examples and RustPrimer two books, please go to more content.

To English Readers

I wrote this in Chinese, so if some links are in Chinese, you can use Google Translate to help it.
English version is done by Google, so thanks Google but sorry for not being perfect.

Chapter 1: Basics

When you start reading, you have some interest.
Getting started with Rust is very simple and doesn’t require any configuration.

You can go to playground to run the code online. The local installation is omitted, please refer to the reference book in the introduction.

Rust Little Book 3 Days(v1.0) - 图1

Here is hello world:

  1. // This is the comment content and will be ignored by the compiler.
  2. fn main() {
  3. println!("Hello Rust World!");
  4. }

println! is a macro (macros) that can output text to the console.

The source program can be used to generate executable files using Rust’s compiler rustc:

  1. $ rustc hello.rs

rustc will get the executable hello after compilation.

  1. $ ./hello
  2. Hello Rust World!

There is a runner that can be run in one step.

In addition, the built-in cargo can provide project management and is introduced in small projects.

Chapter 2: Structure, Trait

First look at the structure:

  1. // unit structure
  2. struct Nil;
  3. // tuple structure
  4. struct Pair(i32, f32);
  5. // structure with two fields
  6. struct Point {
  7. x: f32,
  8. y: f32,
  9. }
  10. // The structure can be used as a field for another structure
  11. #[allow(dead_code)]
  12. struct Rectangle {
  13. p1: Point,
  14. p2: Point,
  15. }
  16. fn main() {
  17. // Instantiate the structure `Point`
  18. let point: Point = Point { x: 0.3, y: 0.4 };
  19. // access the field of point
  20. println!("point coordinates: ({}, {})", point.x, point.y);
  21. // instantiate a unit structure
  22. let _nil = Nil;
  23. // instantiate a tuple structure
  24. let pair = Pair(1, 0.1);
  25. // access the fields of the tuple structure
  26. println!("pair contains {:?} and {:?}", pair.0, pair.1);
  27. // Deconstruct a tuple structure
  28. let Pair(integer, decimal) = pair;
  29. println!("pair contains {:?} and {:?}", integer, decimal);
  30. }

Trait

  1. trait HasArea {
  2. fn area(&self) -> f64;
  3. }
  4. struct Circle {
  5. radius: f64,
  6. }
  7. impl HasArea for Circle {
  8. fn area(&self) -> f64 {
  9. std::f64::consts::PI * (self.radius * self.radius)
  10. }
  11. }
  12. fn main() {
  13. let c = Circle {
  14. radius: 1.0f64,
  15. };
  16. println!("circle c has an area of {}", c.area());
  17. }

This program will output:

  1. circle c has an area of 3.141592653589793

{}, {:?} correspond to two Trait:Display and Debug respectively.

Chapter 3: HashMap, Array

Let’s take a look at the simple usage of HashMap.

  1. use std::collections::HashMap;
  2. fn main(){
  3. let mut come_from = HashMap::new();
  4. // insert
  5. come_from.insert("WaySLOG", "HeBei");
  6. come_from.insert("Marisa", "U.S.");
  7. come_from.insert("Mike", "HuoGuo");
  8. // find the key
  9. if !come_from.contains_key("elton") {
  10. println!("Oh, we found {} personal, but poor Elton cat is still homeless", come_from.len());
  11. }
  12. / / Delete elements according to the key
  13. come_from.remove("Mike");
  14. println!("\nMike猫!\n");
  15. / / use the return of get to determine whether the element exists
  16. let who = ["MoGu", "Marisa"];
  17. for person in &who {
  18. match come_from.get(person) {
  19. Some(location) => println!("{} from: {}", person, location),
  20. None => println!("{} is also homeless.", person),
  21. }
  22. }
  23. // traversal output
  24. println!("\nSo, everyone?");
  25. for (name, location) in &come_from {
  26. println!("{}From: {}", name, location);
  27. }
  28. }

Look at the array again:

  1. fn main() {
  2. let mut array: [i32; 3] = [0; 3];
  3. array[1] = 1;
  4. array[2] = 2;
  5. assert_eq!([1, 2], &array[1..]);
  6. // This loop prints: 0 1 2
  7. for x in &array {
  8. println!("{} ", x);
  9. }
  10. }

Dynamic array Vec

  1. fn main() {
  2. / / Create an empty Vec
  3. let v: Vec<i32> = Vec::new();
  4. println!("{:?}", v);
  5. / / use the macro to create an empty Vec
  6. let v = vec![1, 2, 3, 4, 5];
  7. println!("{:?}", v);
  8. //Create ten zeros
  9. let v = vec![0; 10];
  10. / / Create a variable Vec, and press element 3
  11. println!("{:?}", v);
  12. let mut v = vec![1, 2];
  13. v.push(3);
  14. println!("{:?}", v);
  15. / / Create a Vec with two elements, and pop up an element
  16. let mut v = vec![1, 2];
  17. let two = v.pop();
  18. println!("{:?}", two);
  19. / / Create a variable Vec containing three elements, and index a value and modify a value
  20. let mut v = vec![1, 2, 3];
  21. v[1] = v[1] + 5;
  22. println!("{:?}", v);
  23. }

Special Chapter: 🔥 Ownership, Borrowing and Life Cycle

These concepts of ownership systems are where Rust is different. It is the basis of security, and it is said that compilation will not crash.

The ownership system can be analogized by borrowing books, first look at ownership:

  1. fn main() {
  2. let a: String = String::from("xyz");
  3. let _b = a;
  4. println!("{}", a);
  5. }

This code is ok in traditional languages, and the compilation will report an error because of a transfer of ownership.

Look at borrowing again:

  1. fn main() {
  2. let a: String = String::from("xyz");
  3. let _b = &a;
  4. println!("{}", a);
  5. }

Changed a word is borrowed, you can compile it.

Look at the life cycle:

  1. fn _foo<'a>(x: &'a str) -> &'a str {
  2. x
  3. }

This is a strange life cycle. Some simple cases can omit the life cycle and be more concise. Simply put, the life cycle is the time to specify the variable, when the compiler can not be sure, the programmer will be required to write the life cycle.

Summary

Through three simple examples, three concepts are introduced. Of course, the actual situation is more complicated, but the basic idea is similar.

Rust is through ownership, borrowing, and life.

Chapter 4: Code Organization

Basic usage of the module

  1. mod ccc {
  2. pub fn print_ccc() {
  3. println!("{}", 25);
  4. }
  5. }
  6. fn main() {
  7. use ccc::print_ccc;
  8. print_ccc();
  9. // or
  10. ccc::print_ccc();
  11. }

The package crate is a larger unit.

Really we use a lot of external libraries when we are developing. External library is passed

  1. extern crate xxx;

This was introduced.

Note: For the above references to take effect, you must also add xxx="version num" to the dependecies section of Cargo.toml.

After the introduction, it is equivalent to the introduction of a symbol xxx, which can be directly referenced to the item in the crate with this xxx as the root:

  1. extern crate xxx;
  2. use xxx::yyy::zzz;

When introduced, it can be renamed with the as keyword.

  1. uxtern crate xxx as foo;
  2. use foo::yyy::zzz;

Chapter 5: Rust Highlights

I didn’t want to write anything, it was temporarily blank.

Special chapter: ❌ error handling

Rust solves the problem of null pointers and does not use exceptions.

Rust’s error handling is very special, let’s take a look at the example:

1 simple options

  1. fn get(x: i32) -> Option<i32> {
  2. match x>0 {
  3. true => Some(1),
  4. false => None
  5. }
  6. }
  7. fn main() {
  8. let x : i32;
  9. x = get(1).unwrap();
  10. println!("x has the value {}", x);
  11. }

Option is an Enum type with two values Some and None. In the example, match is handled separately. Unwrap is suitable for demo code and triggers panic directly on None.

2 More complex error handling with Result, see example:

  1. use std::io;
  2. use std::io::Read;
  3. use std::fs::File;
  4. fn read_username_from_file() -> Result<String, io::Error> {
  5. let f = File::open("hello.txt");
  6. let mut f = match f {
  7. Ok(file) => file,
  8. Err(e) => return Err(e),
  9. };
  10. let mut s = String::new();
  11. match f.read_to_string(&mut s) {
  12. Ok(_) => Ok(s),
  13. Err(e) => Err(e),
  14. }
  15. }
  16. fn main() {
  17. let result : Result<String, io::Error>;
  18. result = read_username_from_file();
  19. println!("{:?}", result);
  20. }

Result is also an Enum type, and the two values are Ok and Err, respectively, which can easily convey error information.

3 Simplify the question mark operation of error delivery

  1. use std::io;
  2. use std::io::Read;
  3. use std::fs::File;
  4. fn read_username_from_file() -> Result<String, io::Error> {
  5. let mut f = File::open("hello.txt")?;
  6. let mut s = String::new();
  7. f.read_to_string(&mut s)?;
  8. Ok(s)
  9. }

? The operation is used to simplify the delivery of errors.

You can try to get rid of the tricks at the end, you will find that the compiler accurately found the error, which greatly enhances your confidence in the code, without the extra effort to write unit tests.

Summary:

Through a few simple examples, I learned the error handling roughly.

In summary, there are roughly two ways:

  • Option

  • Result

Option, including Some and None. Suitable for simple situations.

  1. enum Option<T> {
  2. Some(T),
  3. None,
  4. }

Result, including Ok and Err. Used to pass error messages.

  1. enum Result<T, E> {
  2. Ok(T),
  3. Err(E),
  4. }
  1. Simplify the error delivery? Operation, optional. Unwrap is common in the demo code and will be panic. There is also an expect("info"), which is also a panic, but will bring info.

The good news is that the null pointer problem is eliminated. The downside is that it seems complicated and takes time to digest.

In this way, the next topic.

Chapter 6: Concurrency

  1. fn main() {
  2. let mut colors = [-20.0f32, 0.0, 20.0, 40.0,
  3. 80.0, 100.0, 150.0, 180.0, 200.0, 250.0, 300.0];
  4. println!("original: {:?}", &colors);
  5. colors.par_iter_mut().for_each(|color| {
  6. let c : f32 = if *color < 0.0 {
  7. 0.0
  8. } else if *color > 255.0 {
  9. 255.0
  10. } else {
  11. *color
  12. };
  13. *color = c / 255.0;
  14. });
  15. println!("transformed: {:?}", &colors);
  16. }

This example is concurrency through the library and looks simpler.

I still lack experience with concurrency, so I will click on it. Leave it for later discussion.

Chapter 7: Macro

Here is a brief introduction.

  1. macro_rules! create_function {
  2. ($func_name:ident) => (
  3. fn $func_name() {
  4. println!("function {:?} is called", stringify!($func_name))
  5. }
  6. )
  7. }
  8. fn main() {
  9. create_function!(foo);
  10. foo();
  11. }

Chapter 8: Testing

Rust’s test features are divided into three levels according to their granularity:

  1. Function level

  2. Module level

  3. Engineering level

In addition, Rust also supports testing of documents.

Here is a simple function level test.

  1. #[test]
  2. fn it_works() {
  3. assert!(2>1); // do test work
  4. }

Very simple, in Rust, you only need to put a #[test] on top of a function to indicate that this is a function for testing.

With this property, these functions are ignored when compiling with cargo build. These functions can be run using cargo test.

Rust provides two macros to perform test assertions:

  1. assert!(expr) Tests if the expression is true or false
  2. assert_eq!(expr, expr) tests whether the results of two expressions are equal

——Practice small project: Json processing

This project refers to the RustPrimer book, Json Processing. Introduced the use of caogo.

A small project can verify that our understanding of Rust is correct.

in conclusion

I finally finished learning, see how much time you spent, getting started?

I hope to learn happily, wait for the next language, see you next time!