For four years in a row, it has become the "favorite language for developers". Have you ever understood this programming language?

fn main() {   println!("hello rust");}

With the launch of Facebook's Libra project, Rust became the biggest project in the history of Rust, but with the rise of global digital currency, the journey of Rust may be just beginning.

Although you may not know much about Rust, in the eyes of developers, it's really delicious! For four years in a row, in the "favorite programming language" selection of Stack Overflow developers, Rust was the first.

On May 15, 2015, rust officially released version 1.0. Over the past four years, with its two features of "security" and "high concurrency", it has been loved by more and more developers. Rust is occupying the emerging project market of blockchain with overwhelming momentum, and many famous old projects are also considering using rust rewrite.

The language features of Rust (security, high performance, concurrent programming) are naturally compatible with the features of blockchain (distributed, encrypted, security sensitive). Many famous blockchain projects have chosen to use Rust as their development language, including Parity, Polkadot, Substrate, Grin, Ethereum Classic, Holochain, Cardano Rust, Exonum, Lighthouse, Nimiq, Nervos, conflux Rust, Codechain, Witnet, etc., not to mention the upcoming Libra.

I believe that more and more blockchain projects will choose to use Rust as the first development language, and we will usher in a wave of rush language learning climax. I believe that everyone knows how high the pay of blockchain developers is.

There is a [free] Rust course in the experimental building—— Learning from Rust through examples . The course is adapted from the classic textbook "rule by example", and the online experiment environment and challenge test are configured according to the content of the textbook. Every knowledge point has a set of examples and small exercises, so that everyone can easily master the language.

Friends who want to learn can click Learning from Rust through examples oh

Next, let's get familiar with some basic grammar of Rust, and write your first small program with Rust.

This article suggests collecting, so that you can consolidate the basic knowledge of Rust at any time.

Here is Learning from Rust through examples Section I contents:

brief introduction

Rust is a modern system programming language focusing on safety, speed and concurrency. Rust achieves the above goals through memory security, but does not use garbage collection (GC).

This course is the online experimental version of learning Rust through examples. Through a series of online experiments, you can step by step complete the introduction of the programming language of Rust. Welcome to participate in the revision and improvement of the course warehouse. For the warehouse address, please refer to the online experiment version of learn rule through examples. The copyright of all documents follows the copyright of the original documents in Chinese and English (the copyright is MIT or Apache agreement).

Knowledge points

The main contents of the experiment in this section include the following knowledge points:

  • Course introduction
  • How to write the first program
  • Hello World program details
  • notes
  • Format output

Hello World

Our first program will print the legendary "Hello World" message. Here is the complete program code and the compilation and operation process.

This is the source code of the traditional Hello World program. First, create a new one under the / home/project directory in the WebIDE of the experimental building hello.rs File, write the following code (you don't need to enter comments starting with / /):

// This is the comment content, which will be ignored by the compiler
// You can test this code by clicking the button "Run" over there - >
// If you want to use the keyboard, you can use the shortcut key "Ctrl + Enter" to run

// This code supports editing, you can freely modify the code!
// You can return the code to its original state by clicking the Reset button - >

// This is the main function
fn main() {
    // When the compiled executable is called, the statement here will be run.

    // Print text to console
    println!("Hello World!");
}

println! Is a macro that outputs text to the console.

Execute the following command in the WebIDE terminal of the lab building, and use the rustc compiler of Rust to generate the executable file from the source program:

$ cd /home/project
$ rustc hello.rs

After compiling with rustc, you will get the executable hello. Use the following command to run the generated Hello file:

$ ./hello

The results are as follows:

Try it yourself

Please try your hello.rs Add a line of code in the program and use the macro println! Again to get the following results:

Hello World!
I'm a Rustacean!

notes

Annotation is indispensable to any program. Similarly, Rust supports several different ways of annotation.

  • General comments, whose contents will be ignored by the compiler:

  • //Single line comment, comment content up to the end of the line.

  • /*Block comment, comment content all the way to the end separator. * /

  • Document comments whose contents will be parsed into HTML help documents:

  • ///Generate help documents for the next items.

  • //! generates a help document for the item to which the comment belongs.

fn main() {
    // This is an example of line comments
    // Notice that there are two slashes at the beginning of the line
    // Nothing in this will be read by the compiler

    // println!("Hello, world!");

    // Please run it. Do you see the result? Now remove the two slashes from the above statement and run it again.

    /*
     * This is another kind of annotation - block annotation. In general, line comments are the recommended comment format,
     * However, block annotations are particularly useful for temporarily annotating large blocks of code. /*Block comments can be / * nested, */ */
     * So you can comment them out with very few buttons main() Line in function./*/*/* Try it yourself!*/*/*/
     */

     /*
      Note that in the above example, there are "*" in the vertical direction. This is just a style, but it is not necessary.
      */

     // Observe how block annotations simply modify expressions, not line annotations.
     // Deleting the comment separator will change the result.
     let x = 5 + /* 90 + */ 5;
     println!("Is `x` 10 or 100? x = {}", x);
}

Format output

The printing operation is handled by a series of macros defined in std::fmt, including:

  • format!: writes formatted text to a string. String is a return value, not a parameter.
  • print!: similar to format!, but output text to the console (io::stdout).
  • println!: similar to print!, but with a newline appended to the output.
  • eprint!: similar to format!, but output text to a standard error (io::stderr).
  • eprintln!: similar to eprint!, but with a newline appended to the output.

These macros all parse text in the same way. Another advantage is that formatting correctness is checked at compile time.

newly build format.rs File, write the code as follows.

fn main() {
    // In general, the '{}' is replaced by the contents of any variable.
    // The contents of the variable are converted to a string.
    println!("{} days", 31);

    // Without a suffix, 31 automatically becomes the i32 type.
    // You can add a suffix to change the type of 31.

    // There are many ways to replace strings with variables.
    // For example, you can use the location parameter.
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

    // You can use named parameters.
    println!("{subject} {verb} {object}",
             object="the lazy dog",
             subject="the quick brown fox",
             verb="jumps over");

    // You can specify a special format after ':'.
    println!("{} of {:b} people know binary, the other half don't", 1, 2);

    // You can right align text to a specified width.
    // The following statement outputs "1" followed by five spaces.
    println!("{number:>width$}", number=1, width=6);

    // You can fill in 0 to the left of the number. The following statement outputs "00000 1".
    println!("{number:>0width$}", number=1, width=6);

    // println! Checks that the number of parameters used is correct.
    println!("My name is {0}, {1} {0}", "Bond");
    // Correct the missing parameter: "James"

    // Create a Structure containing a single 'i32'. Name it 'Structure'.
    #[allow(dead_code)]
    struct Structure(i32);

    // But custom types like structs need to be handled in a more complex way.
    // The following statement cannot be run.
    println!("This struct `{}` won't print...", Structure(3));
    // Correct ^ to comment out this line.
}

std::fmt contains a variety of traits (traits have the meaning of "characteristic", etc.) to control the text display. The basic forms of two important traits are as follows:

  • fmt::Debug: use the {:?} tag. Format the text for debugging.
  • fmt::Display: use the {} tag. Format text in a more elegant and friendly style.

The above example uses fmt::Display because the standard library provides those types of implementations. To print custom type text, you need more steps.

Try it yourself

  • Correct the two errors in the above code (see "correct" in the code comment) so that it can run without errors.
  • Use a println! Macro to print by controlling the number of decimal places displayed: Pi is roughly 3.142. For practice purposes, let pi = 3.141592 is used as an approximation of PI.

Tip: for decimal display format, please refer to the document std::fmt.

Debug (debug)

For all types, if you want to print out the formatted trait of std::fmt, you need to implement the trait. The automatic implementation is only available for some types, such as those in the std library. All other types must be implemented manually.

fmt::Debug this trait makes this work quite simple. All types can derive the implementation of fmt::Debug. But fmt::Display needs to be implemented manually.

// This structure cannot be printed using 'fmt::Display' or 'fmt::Debug'.
struct UnPrintable(i32);

// `The derive d attribute will automatically create the required implementation so that this' struct 'can print using' fmt::Debug '.
#[derive(Debug)]
struct DebugPrintable(i32);

All std library types are inherently printable using {:?}. Create a new format1.rs file, and write the code as follows:

// Derive the 'fmt::Debug' implementation of 'Structure'.
// `Structure is a structure containing a single 'i32'.
#[derive(Debug)]
struct Structure(i32);

// Put 'Structure' in the Structure 'Deep'. Then make 'Deep' also print.
#[derive(Debug)]
struct Deep(Structure);

fn main() {
    // Using '{:?}' to print is similar to using '{}'.
    println!("{:?} months in a year.", 12);
    println!("{1:?} {0:?} is the {actor:?} name.",
             "Slater",
             "Christian",
             actor="actor's");

    // `Structure 'can also be printed!
    println!("Now {:?} will print!", Structure(3));

    // One problem with using 'derive d' is that you can't control the form of output.
    // What if I just want to show a '7'?
    println!("Now {:?} will print!", Deep(Structure(7)));
}

Compile and run the program in the WebIDE terminal of the experimental building.

$ rustc format1.rs
$ ./format1

The results are as follows:

So fmt::Debug does make this content printable, but at the expense of some beauty. Rust also provides "beautify printing" function through {: ×?}:

#[derive(Debug)]
struct Person<'a> {
    name: &'a str,
    age: u8
}

fn main() {
    let name = "Peter";
    let age = 27;
    let peter = Person { name, age };

    // pretty printer 
    println!("{:#?}", peter);
}

After adding the above code to format1.rs, the results of compiling and running are as follows:

You can control the display effect by implementing fmt::Display manually.

Display

fmt::Debug usually doesn't look very concise, so it's often preferable to customize the appearance of the output. This needs to be done by implementing fmt::Display manually. fmt::Display is marked with {}. The implementation looks like this:

// (use) import the fmt module to make the fmt::Display available
use std::fmt;

// Define a structure, and we will implement 'fmt::Display' for it. Here is a simple tuple structure
// `Structure, which contains an 'i32' element.
struct Structure(i32);

// In order to use the '{}' tag, you must manually implement 'FMT:: display' trait for the type.
impl fmt::Display for Structure {
    // This trait requires' fmt 'to use a function signature that is exactly the same as the following function
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Only the first element of self is written to the given output stream 'f'. Back` fmt:Result `, this
        // The results indicate that the operation was successful or failed. Note that the usage of 'write!' is similar to 'println!'.
        write!(f, "{}", self.0)
    }
}

fmt::Display may be simpler than fmt::Debug, but for the std library, there is a problem. How can ambiguous types be displayed? For example, if the standard library implements the same output style for all VECs, which style should it be? Is one of the following two?

  • Vec: /: / etc:/home/username:/bin (use: split)
  • Vec: 1,2,3 (use, split)

We didn't do this because there wasn't a suitable style for all types, and the standard library didn't dictate a style. For Vec or any other generic container, fmt::Display is not implemented. So in the case of these generics, use fmt::Debug.

This is not a problem because fmt::Display can implement any non generic container type. newly build display.rs The code is as follows:

use std::fmt; // (use) import the fmt module to make the fmt::Display available

// A structure with two numbers. Deduce 'Debug' to compare with the output of 'Display'.
#[derive(Debug)]
struct MinMax(i64, i64);

// Implement 'Display' of 'MinMax'.
impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use` self.number `To represent the data.
        write!(f, "({}, {})", self.0, self.1)
    }
}

// For comparison, define a structure with a named field.
#[derive(Debug)]
struct Point2D {
    x: f64,
    y: f64,
}

// Similarly, 'Display' is implemented for 'Point2D'`
impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Custom format, so that only 'x' and 'y' values are displayed.
        write!(f, "x: {}, y: {}", self.x, self.y)
    }
}

fn main() {
    let minmax = MinMax(0, 14);

    println!("Compare structures:");
    println!("Display: {}", minmax);
    println!("Debug: {:?}", minmax);

    let big_range =   MinMax(-300, 300);
    let small_range = MinMax(-3, 3);

    println!("The big range is {big} and the small is {small}",
             small = small_range,
             big = big_range);

    let point = Point2D { x: 3.3, y: 7.2 };

    println!("Compare points:");
    println!("Display: {}", point);
    println!("Debug: {:?}", point);

    // report errors. `Debug and Display are implemented, but {: b} requires fmt::Binary`
    // It is realized. This statement cannot be run.
    // println!("What does Point2D look like in binary: {:b}?", point);
}

The results of the program are as follows:

fmt::Display is implemented, but fmt::Binary does not, so fmt::Binary cannot be used. std::fmt has many such trait s, which require their own implementation. These are described in detail in the std::fmt chapter later.

Try it yourself

Verify the output of the above example, and then in the example program, add a complex structure following the Point2D structure. Print in the same way, and the output result should look like this:

Display: 3.3 + 7.2i
Debug: Complex { real: 3.3, imag: 7.2 }

Test case: List

To implement fmt::Display for a structure, the elements need to be processed one by one, which may be very troublesome. The problem is that every write! Generates an fmt::Result. The correct implementation needs to handle all results. Rust specifically provides the? Operator to solve this problem.

Use? On write! As follows:

// try 'write!' to see if there is an error. If an error occurs, the corresponding error is returned.
// Otherwise (no errors) continue with the subsequent statement.
write!(f, "{}", value)?;

In addition, you can use try! Macro, which is the same as? Macro. This kind of writing method is verbose, so it is not recommended any more, but it can still be seen in the older Rust code. Using try! Looks like this:

try!(write!(f, "{}", value));

With? It's easy to implement fmt::Display for a Vec. newly build vector.rs The code is as follows:

use std::fmt; // Import the 'fmt' module.

// Define a structure 'List' that contains a single 'Vec'.
struct List(Vec<i32>);

impl fmt::Display for List {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use the subscript of tuple to get the value and create a reference of 'vec'.
        let vec = &self.0;

        write!(f, "[")?;

        // Use 'v' to iterate over 'vec', and use 'count' to record the number of iterations.
        for (count, v) in vec.iter().enumerate() {
            // Comma each element except the first.
            // Use '?' or 'try!' to return an error.
            if count != 0 { write!(f, ", ")?; }
            write!(f, "{}", v)?;
        }

        // Add matching brackets and return an fmt::Result value.
        write!(f, "]")
    }
}

fn main() {
    let v = List(vec![1, 2, 3]);
    println!("{}", v);
}

The results of the program are as follows:

Give it a try:

Change the program so that the subscript of each element in the vector can also be printed out. The new results are as follows:

[0: 1, 1: 2, 2: 3]

format

We have seen that the format is specified by format string:

  • format!("{}", foo) -> "3735928559"
  • format!("0x{:X}", foo) -> "0xDEADBEEF"
  • format!("0o{😮}", foo) -> "0o33653337357"

The same variable (foo) can be formatted in different forms depending on whether the parameter type used is X, o, or unspecified.

The format function is implemented by trait, and each parameter type corresponds to a trait. The most common format trait is Display, which can handle cases where the parameter type is unspecified, such as {}.

Create a new format2.rs file, and write the code as follows:

use std::fmt::{self, Formatter, Display};

struct City {
    name: &'static str,
    // latitude
    lat: f32,
    // longitude
    lon: f32,
}

impl Display for City {
    // `f 'is a buffer. This method must write the formatted string to it
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' };
        let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' };

        // `Write! 'is similar to' format! ', but it will write the formatted string
        // In a buffer (that is, the first parameter f).
        write!(f, "{}: {:.3}°{} {:.3}°{}",
               self.name, self.lat.abs(), lat_c, self.lon.abs(), lon_c)
    }
}

#[derive(Debug)]
struct Color {
    red: u8,
    green: u8,
    blue: u8,
}

fn main() {
    for city in [
        City { name: "Dublin", lat: 53.347778, lon: -6.259722 },
        City { name: "Oslo", lat: 59.95, lon: 10.75 },
        City { name: "Vancouver", lat: 49.25, lon: -123.1 },
    ].iter() {
        println!("{}", *city);
    }
    for color in [
        Color { red: 128, green: 255, blue: 90 },
        Color { red: 0, green: 3, blue: 254 },
        Color { red: 0, green: 0, blue: 0 },
    ].iter() {
        // After adding an implementation for fmt::Display, use {} to verify the effect instead.
        println!("{:?}", *color)
    }
}

The results of the program are as follows:

You can view the formatted traits list and their parameter types in the fmt::fmt document.

Try it yourself

To implement fmt::Display for the above Color structure, you should get the following output results:

RGB (128, 255, 90) 0x80FF5A
RGB (0, 3, 254) 0x0003FE
RGB (0, 0, 0) 0x000000

If you are confused, here are two tips:

  • You may need to list each color several times,
  • You can use: 02 to make the number 2.

Experiment summary

In this section, we learned the following contents:

  • Course introduction
  • How to write the first program
  • Hello World program details
  • notes
  • Format output

If you want to continue learning, click Learning from Rust through examples Free learning!

Tags: Blockchain Programming Apache Attribute

Posted on Sat, 30 May 2020 06:08:29 -0400 by ucffool