# 不仅仅是Hello world

几乎所有教程中安装的最后一个环节都是`hello world`，我们也不能免俗，但是在`hello world`之后，还有一个相亲，阿呸，Rust初印象环节，希望大家喜欢。

## 多国语言的"世界，你好"

还记得大明湖畔等你的[VScode](./editor.md) IDE和通过Cargo创建的[世界，你好](./cargo.md)工程嘛? 

现在使用VScode打开[上一节](./cargo.md)中创建的`world_hello`工程, 然后进入`main.rs`文件，此文件是当前Rust工程的入口文件，和其它语言几无区别。

接下来，对世界友人给予热切的问候：
```rust
fn greet_world() {
     let southern_germany = "Grüß Gott!";
     let chinese = "世界，你好";
     let english = "World, hello";
     let regions = [southern_germany, chinese, english];
     for region in regions.iter() {
             println!("{}", &region);
     }
 }
 
 fn main() {
     greet_world();
 }
```

打开终端，进入`world_hello`工程根目录，运行该程序(你也可以在VScode中打开终端,方法是点击左下角的错误和警告图标)，你的热情，好像一把火，燃烧了整个世界：
```console
$ cargo run
   Compiling world_hello v0.1.0 (/Users/sunfei/development/rust/world_hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
     Running `target/debug/world_hello`
Grüß Gott!
世界，你好
World, hello
sunfei@sunfeideMa
```

花点时间来看看上面的代码，首先，Rust原生支持UTF-8编码的字符串，这意味着你可以很容易的使用世界各国文字作为字符串内容。

其次，关注下`println`后面的`!`，如果你有Ruby编程经验，那么你可能会认为这是解构操作符，但是在Rust中，这是`宏`操作符，你目前可以认为宏是一种特殊类型函数。

对于`println`来说，我们没有使用其它语言惯用的`%s`,`%d`来做输出占位符，而是使用`{}`，因为Rust在底层帮我们做了大量工作，会自动识别输出数据的类型，例如当前例子，会识别为`string`类型。

最后，和其它语言不同，rust的集合类型不能直接进行循环，需要变成迭代器(这里是通过`.iter()`方法)，才能用于迭代循环，在目前来看，你会觉得这一点好像挺麻烦，不急，以后就知道这么做的好处所在.

至于函数声明、调用、数组的使用，和其它语言没什么区别，so easy!

## Rust语言初印象
Rust这门语言对于Haskell和Java开发来说，会觉得很熟悉，因为它们在高阶表达方面都很优秀，简而言之，可以很简洁的写出原本需要一大堆代码才能表达的含义，但是Rust又有所不同：它的性能是底层语言级别的性能，可以跟C/C++相媲美。

上一句的`so easy`的余音仍在绕梁，我希望它能继续下去，可是。。。人总是要面对现实，因此让我们来些狠的: 
```rust
fn main() {
   let penguin_data = "\
   common name,length (cm)
   Little penguin,33
   Yellow-eyed penguin,65
   Fiordland penguin,60
   Invalid,data
   ";
 
   let records = penguin_data.lines();
 
   for (i, record) in records.enumerate() {
     if i == 0 || record.trim().len() == 0 {
       continue;
     }
     
     // 声明一个fields变量，类型是Vec
     // Vec是vector的缩写，是一个可伸缩的集合类型，可以认为是一个动态数组
     // <_>表示Vec中的元素类型由编译器自行推断，在很多场景下，都会帮我们省却不少功夫
     let fields: Vec<_> = record
       .split(',')
       .map(|field| field.trim())
       .collect();
     if cfg!(debug_assertions) {
         // 输出到标准错误输出
       eprintln!("debug: {:?} -> {:?}",
              record, fields);
     }
 
     let name = fields[0];
     // 1. 尝试把fields[1]的值转换为f32类型的浮点数，如果成功，则把f32值赋给length变量
     // 2. if let是一个匹配表达式，用来从=右边的结果中，匹配出length的值:
     // 当=右边的表达式执行成功，则会返回一个Ok(f32)的类型，若失败，则会返回一个Err(e)类型，if let的作用就是仅匹配Ok也就是成功的情
     // 况,如果是错误，就直接忽略，同时if let还会做一次解构匹配，通过Ok(length)去匹配右边的Ok(f32)，最终把相应的f32值赋给length
     // 3. 当然你也可以忽视成功的情况，用if let Err(e) = fields[1].parse::<f32>() {...}匹配出错误，然后打印出来，但是没啥卵用
     if let Ok(length) = fields[1].parse::<f32>() {
         // 输出到标准输出
         println!("{}, {}cm", name, length);
     }
   }
 }
```

看完这段代码，不知道你的余音有没有嘎然而止，反正我已经在颤抖了，这就是传说中的下马威嘛？上面代码中值得注意的Rust特性有：
- 控制流：`for`和`continue`在一起，实现的循环
- 方法语法：由于Rust没有继承，因此Rust不是传统意义上的面向对象语言，但是它却从`OO`语言那里偷师了方法的使用`record.trim()`,`record.split(',')`等
- 高阶函数编程： 函数可以作为参数也能作为返回值，例如`.map(|field| field.trim())`, 这里`map`使用闭包函数作为参数，也可以称呼为`匿名函数`、`lambda函数`
- 类型标注: `if let Ok(length) = fields[1].parse::<f32>()`, 通过`::<f32>`的使用，告诉编译器`length`是一个`f32`类型的浮点数，这种类型标注不是很常用，但是在编译器无法推断出你的数据类型时，就很有用了
- 条件编译: `if cfg!(debug_assertions)`，说明紧跟其后的输出打印只在`debug`模式下生效
- 隐式返回：Rust提供了`return`关键字用于函数返回，但是在很多时候，我们可以省略它。因为Rust是[**基于表达式的语言**](../basic/base-type/statement-expression.md)


在终端运行上述代码时，会看到很多`debug: ...`的输出, 上面有讲，这些都是`条件编译`的输出, 那么该怎么消除掉这些输出呢？

读者大大普遍冰雪聪明，肯定已经想到：是的，在[认识Cargo](./cargo.md#手动编译和运行项目)中，曾经介绍过`--relese`参数，因为`cargo run`默认是运行`debug`模式. 因此想要消灭那些`debug:`输出，需要更改为其它模式，其中最常用的模式就是`--release`也就是生产发布的模式。

具体运行代码就不给了，留给大家作为一个小练习，建议亲自动手尝试下。

至此，Rust安装入门就已经结束，相信看到这里，你已经发现了本书与其它书的区别，其中最大的区别就是：**这本书就像优秀的国外课本一样，不太枯燥，也希望这本不太枯燥的书，能伴你长行，犹如一杯奶茶，细细品之，唇齿余香**。
