读Rust By Example(1)

Rust是注重于安全、效率和并发的现代系统编程语言。在不使用垃圾回收机制的前提下保证内存安全并达到以上目标。

Hello World

// This is a comment, and will be ignored by the compiler

// This is the main function
fn main() {  
    // The statements here will be executed when the compiled binary is called

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

println!是终端打印一行的宏。

可以用Rust的编译器rustc来生成二进制文件,产生的二进制文件可以被执行。

$ rustc hello.rs

练习:输出以下文字

Hello World!  
I'm a Rustacean!  

解答:

fn main() {  
    println!("Hello World!");
    println!("I'm a Rustacean!");
}
Hello World!  
I'm a Rustacean!  

Comment 注释

Rust包含的注释种类:

  1. // 行注释
  2. /**/ 块注释
  3. /// 库文档行注释
  4. //! 库文档块注释

Formatted print 格式化打印

打印是由一系列在std::fmt中定义的一系列macros来处理的,包括:

  1. format!: 输出格式化文字为String
  2. print!: 将如format!后的格式化文字打印在终端上
  3. println!: 如print!,但增加一个新行

示例代码:

fn main() {  
    // In general, the `{}` will be automatically replaced with any
    // arguments. These will be stringified.
    println!("{} days", 31);

    // Without a suffix, 31 becomes an i32. You can change what type 31 is,
    // with a suffix.

    // There are various optional patterns this works with. Positional
    // arguments can be used.
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

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

    // Special formatting can be specified after a `:`.
    println!("{} of {:b} people know binary, the other half don't", 1, 2);

    // You can right-align text with a specified width. This will output
    // "     1". 5 white spaces and a "1".
    println!("{number:>width$}", number=1, width=6);

    // You can pad numbers with extra zeroes. This will output "000001".
    println!("{number:>0width$}", number=1, width=6);

    // It will even check to make sure the correct number of arguments are
    // used.
    println!("My name is {0}, {1} {0}", "Bond", "James");
    // FIXME ^ Add the missing argument: "James"

    // Create a structure which contains an `i32`. Name it `Structure`.
    #[allow(dead_code)]
    struct Structure(i32);

    // However, custom types such as this structure require more complicated
    // handling. This will not work.
    //println!("This struct `{}` won't print...", Structure(3));
    // FIXME ^ Comment out this line.
}

通过以上代码可以看出许多特性:

  1. {}可以被任意参数代替
  2. 如果没有手动添加后缀,样例中的31将会默认被指定为i32类型
  3. 可以给{}指定下标,灵活使用
  4. 同时也可以给{}的参数命名
  5. 可以利用:特殊地为参数改变格式,如{:b}将参数转换为二进制
  6. 可以格式化右对齐输出参数,如println!("{number:>width$}", number=1, width=6);
  7. 可以用0填充参数,如println!("{number:>0width$}", number=1, width=6);
  8. {}会进行参数下标检查,如果提供的参数小于下标,将会报错

std::fmt中包含了许多traits用于管理文本显示。两个重要的基本形式如下所示:

  1. fmt::Debug: 使用{:?}标记,用于调试
  2. fmt::Display: 使用{}标记,以更加用户友好的方式格式化

练习:使用println!宏打印Pi is roughly 3.142,通过let pi = 3.141592,使输出的文本符合字符串。

解答:

fn main(){  
    let pi = 3.141592;
    println!("Pi is roughly {:.3}", pi);
}
Pi is roughly 3.142  
Debug

只有在std库中的类型才能够被自动实现,其他的一定要通过手动实现。所有的类型都能通过fmt::Debug实现派生,而fmt::Display需要手动实现。

fmt::Displayfmt::Debug都无法打印这样的结构

struct UnPrintable(i32);  

derive 属性自动创建了fmt::Debug的实现,使结构可以打印

#[derive(Debug)]
struct DebugPrintable(i32);  

示例如下:

// Derive the `fmt::Debug` implementation for `Structure`. `Structure`
// is a structure which contains a single `i32`.
#[derive(Debug)]
struct Structure(i32);

// Put a `Structure` inside of the structure `Deep`. Make it printable
// also.
#[derive(Debug)]
struct Deep(Structure);

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

    // `Structure` is printable!
    println!("Now {:?} will print!", Structure(3));

    // The problem with `derive` is there is no control over how
    // the results look. What if I want this to just show a `7`?
    println!("Now {:?} will print!", Deep(Structure(7)));
}

以上例子可以看出,无论是std库中的基本类型还是通过fmt::Debug派生出来的本无法直接打印的结构类型都可以通过println!宏输出。通过手动派生来实现fmt::Display可以修复不那么优美的输出。

Display

通过实现fmt::Display来自定义输出格式,即使用{}标记。

// 导入`fmt`模块
use std::fmt;

// 定义一个将要实现`fmt::Display`的结构
struct Structure(i32);

// 手动实现`fmt::Display`
impl fmt::Display for Structure {  
    // This trait requires `fmt` with this exact signature.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 将第一个元素直接写入提供的输出
        // 流: `f`. 返回 `fmt::Result` 判断操作是否成功
        // `write!`和`println!`使用近似的语法
        write!(f, "{}", self.0)
    }
}

fmt::Display不对Vec<T>和其他容器做实现,只有fmt::Debug才能使用。但是实际上fmt::Display都可以被这些容器实现。

use std::fmt; // Import `fmt`

// 包含两个数字的结构.
// 派生一个 `Debug` 来与 `Display`做对比
#[derive(Debug)]
struct MinMax(i64, i64);

// 对 `MinMax` 做 `Display` 的实现
impl fmt::Display for MinMax {  
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 使用 `self.number` 来指定输出的下标
        write!(f, "({}, {})", self.0, self.1)
    }
}

// 定义一个可命名域的结构做比较
#[derive(Debug)]
struct Point2 {  
    x: f64,
    y: f64,
}

// 也对 Point2 做实现
impl fmt::Display for Point2 {  
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 由于自定义了字段名,只能用 `x` 和`y` 来指定
        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 = Point2 { x: 3.3, y: 7.2 };

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

    // Error. Both `Debug` and `Display` were implemented but `{:b}`
    // requires `fmt::Binary` to be implemented. This will not work.
    // println!("What does Point2D look like in binary: {:b}?", point);
}

练习:如上所示,利用Point2来添加一个复数,输出的结构为:

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

解答:

use std::fmt;

#[derive(Debug)]
struct Complex{  
    real: f64,
    imag: f64,
}

impl fmt::Display for Complex{  
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
        write!(f, "real: {}, imag: {}i", self.real, self.imag)
    }
}

fn main() {  
    let complex = Complex{real: 3.3, imag: 7.2};
    println!("Compare structure:");
    println!("Display: {}", complex);
    println!("Debug: {:?}", complex);
}

输出:

Compare structure:  
Display: real: 3.3, imag: 7.2i  
Debug: Complex { real: 3.3, imag: 7.2 }  
Testcase: List

每一个write!宏都产生一个fmt::Result。Rust提供了try!宏来针对这样的情况。

如果try!是可用的,那么对Vec的一个fmt::Display实现就很直观:

use std::fmt; 

// 定义一个包含`Vec`的List容器
struct List(Vec<i32>);

impl fmt::Display for List {  
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 去`self`的引用并且创建`vec`的引用
        let List(ref vec) = *self;
        // 左括号
        try!(write!(f, "["));

        // 通过`v`来迭代`vec`, `count`来计数
        for (count, v) in vec.iter().enumerate() {
            // 对于第一个元素外的所有元素,都添加一个逗号
            // 在调用 `write!` 之前,使用 `try!` 返回错误
            if count != 0 { try!(write!(f, ", ")); }
            try!(write!(f, "{}", v));
        }

        // 返回 fmt::Result,切记不要分号
        write!(f, "]")
    }
}

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

练习:更改输出使其得到如下结果

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

解答:

use std::fmt;

struct List(Vec<i32>);

impl fmt::Display for List {  
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let List(ref vec) = *self;

         try!(write!(f, "["));

         for(count, v) in vec.iter().enumerate(){
             //这代码丑的不行
             write!(f, "{}: ", count);
             try!(write!(f, "{}", v));
             if count != vec.len()-1 {
                 try!(write!(f, ", "));
             }
         }

         write!(f, "]")
     }
}

fn main() {  
    let v = List(vec!(2, 4, 6, 8, 10, 15, 30));
    println!("{}", v);
}

输出结果:

[0: 2, 1: 4, 2: 6, 3: 8, 4: 10, 5: 15, 6: 30]
格式化

通过format!宏来格式化得到特定字符串

  • format!("{}", foo) -> "3735928559" //十进制(默认)
  • format!("0x{:X}", foo) -> "0xDEADBEEF" //十六进制
  • format!("0o{:o}", foo) -> "0o33653337357" //八进制

样例:

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 into 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 like `format!`, but it will write the formatted string
        // into a buffer (the first argument)
        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() {
        // Switch this to use {} once you've added an implementation
        // for fmt::Display
        println!("{:?}", *color)
    }
}

输出为

Dublin: 53.348°N 6.260°W  
Oslo: 59.950°N 10.750°E  
Vancouver: 49.250°N 123.100°W  
Color { red: 128, blue: 255, green: 90 }  
Color { red: 0, blue: 254, green: 3 }  
Color { red: 0, blue: 0, green: 0 }  

练习:对结构Color增加一个fmt::Display的实现,得到以下输出

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

解答:

use std::fmt;

struct Color {  
    red: u8,
    green: u8,
    blue: u8,
}

impl fmt::Display for Color {  
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
         //两点要注意的地方
         //1. 用0填充不足两位的十六进制数
         //2. 输出的十六进制用大写的X表示
         write!(f, "RGB ({}, {}, {}) 0x{:>02X}{:>02X}{:>02X}", self.red, self.green, self.blue, self.red, self.green, self.blue)
     }
}

fn main() {  
    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(){
        println!("{}", color);
    }
}