您的位置:首页 > 编程语言

边做边学Rust之Hello World

2015-11-16 08:45 731 查看

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支持几种注释:

常规注释会被编译器忽略:
// 行注释到行尾结束。
/* 块注释到分隔符结束。 */
文档注释被解析到HTML库文档:
/// 为其下面的项目产生库文档。
//! 为其下面的闭包项目产生文档。

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息