边做边学Rust之Hello World

1. Hello World

这是一个传统Hello World程序的源码:

// This is a comment, and will be ignored by the compiler
// You can test this code by clicking the "Run" button over there ->
// or if prefer to use your keyboard, you can use the "Ctrl + Enter" shortcut

// This code is editable, feel free to hack it!
// You can always return to the original code by clicking the "Reset" button ->

// 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

rustc可以生成一个可执行的二进制程序hello。


$ ./hello
Hello World!

1.1 注释

任何一个程序都需要注释并且Rust支持几种注释:

  • 常规注释会被编译器忽略:
  1. // 行注释到行尾结束。
  2. /* 块注释到分隔符结束。 */
  • 文档注释被解析到HTML库文档:
  1. /// 为其下面的项目产生库文档。
  2. //! 为其下面的闭包项目产生文档。
fn main() {
    // This is an example of a line comment
    // Notice how there are two slashes at the beginning of the line
    // And that nothing written inside these will be read by the compiler

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

    // Run it. See? Now try deleting the two slashes, and run it again.

    /*
     * This is another type of comment, the block comment. In general,
     * the line comment is the recommended comment style however the
     * block comment is extremely useful for debugging
     */

     /*
     Note, the previous column of `*` was entirely for style. There's
     no actual need for it.
     */

     // Observe how block comments allow easy expression manipulation
     // which line comments do not. Deleting the comment deliminators
     // will change the result:
     let x = 5 + /* 90 + */ 5;
     println!("Is `x` 10 or 100? x = {}", x);
}

程序输出的结果是:

Is `x` 10 or 100? x = 10

1.2 格式输出


打印通过定义在std::fmt中的一系列宏来处理,这些宏包括:

  • format!:将格式化文本写入String
  • print!   :与format! 类似,但是文本信息会打印到控制台
  • 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} {predicate}",
             predicate="over the lazy dog",
             subject="the quick brown fox",
             verb="jumps");

    // 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`.
    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.
}

程序输出结果为:


31 days
Alice, this is Bob. Bob, this is Alice
the quick brown fox jumps over the lazy dog
1 of 10 people know binary, the other half don't
1
000001
My name is Bond, James Bond

std::fmt 包含了许多主导显示方式的方法。下面是重要的两个:
  • fmt::Debug:使用{:?}标识。为了调试目的而存在的文本格式。
  • fmt::Display:使用{}标识。更优雅的用户友好的格式化文本。
这里使用fmt::Display是因为std标准库为这些类型提供了实现。要是来打印用户自定义的类型,则还有许多工作要做。

1.2.1 Debug


所有想使用std::fmt格式特定的类型需要对可打印进行实现。仅仅标准库类型提供了自动实现。所有其他的类型需要手动实现。


fmt::Debug特性对这个实现非常直接。所有的类型能够derive(自动创建)fmt::Debug的实现。fmt::Display并不是这样的,需要手动实现。
// This structure cannot be printed either with `fmt::Display` or
// with `fmt::Debug`
struct UnPrintable(i32);

// The `derive` attribute automatically creates the implementation
// required to make this `struct` printable with `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)));
}

打印结果:


12 months in a year.
"Christian" "Slater" is the "actor\'s" name.
Now Structure(3) will print!
Now Deep(Structure(7)) will print!

对上面用户自定义类型,fmt::Debug确实是可打印的,但是有失优雅。手动实现fmt::Display将能修正它。


1.2.2 Dispaly显示


fmt::Debug看起来并不简洁和整洁,所以定制化输出常常是有益的。可以通过手动实现使用{}作为打印标识的fmt::Display来完成,实现看起来是这样的:


// Import (via `use`) the `fmt` module to make it available.
use std::fmt;

// Define a structure which `fmt::Display` will be implemented for. This is simply
// a tuple struct containing an `i32` bound to the name `Structure`.
struct Structure(i32);

// In order to use the `{}` marker, the trait `fmt::Display` must be implemented
// manually for the type.
impl fmt::Display for Structure {
    // This trait requires `fmt` with this exact signature.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Write strictly the first element into the supplied output
        // stream: `f`. Returns `fmt::Result` which indicates whether the
        // operation succeeded or failed. Note that `write!` uses syntax which
        // is very similar to `println!`.
        write!(f, "{}", self.0)
    }
}

fmt::Display或许比fmt::Debug整洁,但是这对于标准库来说会有一个问题。模糊类型会被怎样显示?例如,标准库为所有的Vec<T>提供了单一格式的实现,该是什么样的类型?或者都不是?
  • Vec<path>: /:/etc:/home/username:/bin (以:分割)
  • Vec<number>: 1,2,3 (以,分割)
因为对所有的类型来说没有理想的格式,标准库并不会假设一个。Rust并没有为Vec<T>或者其他的通用容器实现fmt::Display。这些通用的case必须使用fmt::Display。

尽管这样,这不是一个问题,因为对于任何一个新的容器,如果它不是通用则可以实现fmt::Display。

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

// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);

// Implement `Display` for `MinMax`.
impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use `self.number` to refer to each positional data point.
        write!(f, "({}, {})", self.0, self.1)
    }
}

// Define a structure where the fields are nameable for comparison.
#[derive(Debug)]
struct Point2 {
    x: f64,
    y: f64,
}

// Similarly, implement for Point2
impl fmt::Display for Point2 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Customize so only `x` and `y` are denoted.
        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);
}

程序执行结果为:


Compare structures:
Display: (0, 14)
Debug: MinMax(0, 14)
The big range is (-300, 300) and the small is (-3, 3)
Compare points:
Display: x: 3.3, y: 7.2
Debug: Point2 { x: 3.3, y: 7.2 }

我们实现了fmt::Display但是没有实现fmt::Binary,因此不能使用。std::fmt有许多这样的特性并且每一个都需要自己实现。详细信息可以在std::fmt中看到。


1.2.2.1 测试用例:List


为一个必须顺序处理的结构实现fmt::Display是棘手的。问题是每一个write!产生一个fmt::Result。适当的处理这个问题需要处理所有的结果。Rust提供了try!宏来实现这个目标。


在write!上使用try!看起来是这样的:

// Try `write!` to see if it errors. If it errors, return
// the error. Otherwise continue.
try!(write!(f, "{}", value));

使用try!实现一个Vec的fmt::Display是这样的:

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

// Define a structure named `List` containing a `Vec`.
struct List(Vec<i32>);

impl fmt::Display for List {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Dereference `self` and create a reference to `vec`
        // via destructuring.
        let List(ref vec) = *self;

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

        // Iterate over `vec` in `v` while enumerating the iteration
        // count in `count`.
        for (count, v) in vec.iter().enumerate() {
            // For every element except the first, add a comma
            // before calling `write!`. Use `try!` to return on errors.
            if count != 0 { try!(write!(f, ", ")); }
            try!(write!(f, "{}", v));
        }

        // Close the opened bracket and return a fmt::Result value
        write!(f, "]")
    }
}

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

程序输出为:


[1, 2, 3]


1.2.2 Dispaly显示


fmt::Debug看起来并不简洁和整洁,所以定制化输出常常是有益的。可以通过手动实现使用{}作为打印标识的fmt::Display来完成,实现看起来是这样的:


// Import (via `use`) the `fmt` module to make it available.
use std::fmt;

// Define a structure which `fmt::Display` will be implemented for. This is simply
// a tuple struct containing an `i32` bound to the name `Structure`.
struct Structure(i32);

// In order to use the `{}` marker, the trait `fmt::Display` must be implemented
// manually for the type.
impl fmt::Display for Structure {
    // This trait requires `fmt` with this exact signature.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Write strictly the first element into the supplied output
        // stream: `f`. Returns `fmt::Result` which indicates whether the
        // operation succeeded or failed. Note that `write!` uses syntax which
        // is very similar to `println!`.
        write!(f, "{}", self.0)
    }
}

fmt::Display或许比fmt::Debug整洁,但是这对于标准库来说会有一个问题。模糊类型会被怎样显示?例如,标准库为所有的Vec<T>提供了单一格式的实现,该是什么样的类型?或者都不是?
  • Vec<path>: /:/etc:/home/username:/bin (以:分割)
  • Vec<number>: 1,2,3 (以,分割)
因为对所有的类型来说没有理想的格式,标准库并不会假设一个。Rust并没有为Vec<T>或者其他的通用容器实现fmt::Display。这些通用的case必须使用fmt::Display。

尽管这样,这不是一个问题,因为对于任何一个新的容器,如果它不是通用则可以实现fmt::Display。

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

// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);

// Implement `Display` for `MinMax`.
impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use `self.number` to refer to each positional data point.
        write!(f, "({}, {})", self.0, self.1)
    }
}

// Define a structure where the fields are nameable for comparison.
#[derive(Debug)]
struct Point2 {
    x: f64,
    y: f64,
}

// Similarly, implement for Point2
impl fmt::Display for Point2 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Customize so only `x` and `y` are denoted.
        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);
}

程序执行结果为:

Compare structures:
Display: (0, 14)
Debug: MinMax(0, 14)
The big range is (-300, 300) and the small is (-3, 3)
Compare points:
Display: x: 3.3, y: 7.2
Debug: Point2 { x: 3.3, y: 7.2 }

我们实现了fmt::Display但是没有实现fmt::Binary,因此不能使用。std::fmt有许多这样的特性并且每一个都需要自己实现。详细信息可以在std::fmt中看到。


1.2.3 格式


我们已经看到格式通过一个格式化字符串(format String)来指定:


  • format!("{}", foo) -> "3735928559"
  • format!("0x{:X}", foo) -> "0xDEADBEEF"
  • format!("0o{:o}", foo) -> "0o33653337357"
同一个变量(foo)根据不同的参数类型能被格式化为不同的格式:X VS o VS unspecified(未指定类型)。

这种格式化功能通过特性实现,并且每种参数类型都有一个特性。最普通的格式化特性就是Display,它来出来那些未指定的参数类型:例如{}

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)
    }
}

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);
    }
}

程序输出结果:


Dublin: 53.348°N 6.260°W
Oslo: 59.950°N 10.750°E
Vancouver: 49.250°N 123.100°W

下面是所有的格式化特性和与之相对应的参数类型:

  • unspecified -> Display
  • ? -> Debug
  • o -> Octal
  • x -> LowerHex
  • X -> UpperHex
  • p -> Pointer
  • b -> Binary
  • e -> LowerExp
  • E -> UpperExp







  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值