写给前端看的Rust教程(4)Hello World

2,125 阅读3分钟

原文:24 days from node.js to Rust

前言

如果你从没和编辑成二进制的语言打过交道,用Rust的时候也不会感到困难,Rust对不同环境下可执行文件的编译做了很好的支持,让程序的移植变得更加轻松,想要了解更多可见:Cross-compilation

Hello world

创建项目

首先我们用cargo new创建一个Rust项目:

cargo new rust-test

默认情况下,cargo new会创建一个二进制应用的模板,这也是我们现在所需要的,不过需要留意的是,你也可以通过cargo new --lib命令来创建一个库模板

执行完上面的命令,你会得到这样一个项目:

rust-test/
├── .git
├── .gitignore
├── Cargo.toml
└── src
    └── main.rs

我们使用的Rust版本是2021,如果你的Cargo.toml文件中edition的值是2018那么就要留意了,本文都是以2021版为准(20182021之间的差异很大,edition类似ECMAScript versions

打包&运行

现在我们可以尝试运行下这个项目,执行cargo run命令:

   $ cargo run
   Compiling rust-test v0.1.0 (\rust-test)
    Finished dev [unoptimized + debuginfo] target(s) in 1.28s
     Running `target\debug\rust-test.exe`
Hello, world!

cargo run会调用cargo build来打包你的应用,然后运行默认(也可以指定)的二进制文件,运行完这个命令后,会生成好二进制文件,具体的位置是./target/debug/my-app。如果你只是想打包而不不运行,可以执行cargo build命令。默认情况下会生成文件大小、性能等debug信息,当你想要得到release版代码时,可以运行cargo build --release,此时二进制文件的生成目录是./target/release/rust-test

源码分析

下面我们来看下模板中的src/main.rs文件

fn main() {
  println!("Hello, world!");
}

main()函数在独立可执行文件中是不可或缺的,是程序的入口。println!()是一个宏,用于将参数输出到STDOUT,关于宏我们后面会再详细介绍。"Hello, world!"是一段字符串,这很rust,因为字符串将是学习rust的第一个拦路虎,下面就让我们具体了解下

字符串问题1

首先我们将"Hello, world!"赋值给一个let修饰的变量(rust中采用letconst作为关键词,这点类似JavaScript。不过如果你在JavaScript中使用const的情景,在rust绝大大多数下要使用let

fn main() {
  let greeting = "Hello, world!";
  println!(greeting);
}

此时,如果你的VS Code按照上一篇文章所介绍的那样做好了配置的话,此时你已经能在VS Code中看到错误提示了

image.png

忽略这个提示,试着执行下cargo run试试看:

$ cargo run
  
  Compiling rust-test v0.1.0 (rust-test)
error: format argument must be a string literal
 --> src\main.rs:3:14
  |
3 |     println!(greeting);
  |              ^^^^^^^^
  |
help: you might be missing a string literal to format with
  |
3 |     println!("{}", greeting);
  |              +++++

error: could not compile `rust-test` due to previous error

如果你希望上面的代码能正常运行,那你就是个“正常的”程序员,因为在很多语言里这是行的通的,字符串就是字符串。但是在rust的世界这是不行的。在编写rust代码时要习惯查看报错信息,报错信息不仅会告诉你遇到了什么问题,还会告诉你如何修复。println!()的第一个参数需要是个字符串,并且支持将其用变量格式化,将代码进行如下的改动:

fn main() {
  let greeting = "Hello, world!";
+  println!("{}", greeting);
}

再次执行cargo run,此时运行正确

字符串问题2

对于经验老到的程序员来说,希望可以抽象代码以便进行复用,很可能一开始会写出这样的代码:

fn main() {
  greet("World");
}

fn greet(target: String) {
  println!("Hello, {}", target);
}

按照以往的编程经验来看,上面这段代码应该没啥问题,但一执行就会发现问题:

$ cargo run
  
  Compiling rust-test v0.1.0 (rust-test)
error[E0308]: mismatched types
 --> src\main.rs:2:11
  |
2 |     greet("World");
  |           ^^^^^^^- help: try using a conversion method: `.to_string()`
  |           |
  |           expected struct `String`, found `&str`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `rust-test` due to previous error

按照提示执行rustc --explain E0308可以得到更多的信息,我们可以通过如下方式进行修复:

fn main() {
+    greet("World".to_string());
}

fn greet(target: String) {
    println!("Hello, {}", target);
}

具体原因详见 String vs &str in Rust

总结

rust中需要大脑时时刻刻对strings保持警惕,在下文中我们将要介绍所有权(ownership)的概念,这可以帮你解答本文中关于strings的一些疑惑

更多