Part 1: Getting oriented
Rust 源码文件基本结构
|- src
| |- main.rs
|- Cargo.toml
其中 Cargo.toml
文件内容
[package]
name = "hello-world"
version = "0.1.0"
authors = ["Ryan Eberhardt <reberhardt7@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
main.rs
文件内容
fn main() {
println!("Hello, world!");
}
编译命令
$ cargo build
生成二进制文件为 target/debug/hello-world
,文件名称由 toml
文件中的 name
设置
运行命令
$ cargo run
Part 2: Rust warmup
语法
Numeric types
有符号类型为 i8
, i16
, i32
, i64
,i
表示 int
,数字表示 bit 长度
对应的无符号类型为 u8
, u16
, u32
, u64
,u
表示 unsigned
使用 let
声明变量
// int n = 1;
let n: i32 = 1;
Type inference
编译器可以自动识别变量类型,只有无法确定时,才需要显式声明
let n = 1;
variable and constants
数据类型默认是 const
,定义变量时需要使用 mut
关键字
let mut n = 0;
n = n + 1;
Strings
字符串有两种类型,分别为 &str
和 String
&str
指向内存中不可变的字符串(可以缺省),例如定义一个只读字符串:
let s: &str = "Hello world";
String
类型保存在 heap 中,需要使用 mut
,String
可缺省
let mut s: String = String::from("Hello");
s.push_str("world!");
在
String
追加内容后,编译器会自动根据需要realloc
字符串所在的堆在最后一次使用后,编译器会自动进行
free
Data Struct
vector
let mut v: Vec<i32> = Vec::new();
v.push(2);
v.push(3);
array
rust 中的数组长度是可变的
let mut arr: [i32; 4] = [0, 2, 4, 8];
arr[0] = -2;
println!("{}", arr[0] + arr[1]);
Loop
可以使用 in
遍历数组内容
for i in v.iter() {
println!("{}", i);
}
While
循环
while i < 20 {
i += 1;
}
为 While true
提供了特殊的 loop
循环,这个关键字有助于编译器对变量初始化做一些推测
loop {
i += 1;
if i == 10 { break; }
}
Function
- 需要指定函数类型,编译器无法推测
- 函数可以没有
return
关键字,最后不加分号即可
fn sum(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
// do stuff...
}
rust 中所有东西都是表达式,主要是受到了函数式编程的影响
// int x = someBool ? 2 : 4;
let x = if someBool {2} else {4};
在这一思想的影响下,函数是一个由分号分隔的表达式序列,并对最后一个表达式的值进行评估
所以结尾的 无分号单一表达式 会被认为是返回值
例如:
fn fib(n: i32) -> i32 {
if n <= 1 { n } else { fib(n-1) + fib(n-2) }
}
练习
实现三个函数
add_n
: 输入一个 vec 和数字 n,为 vec 中每个数字加上 n,并返回一个新 vec
add_n_inplace
: 直接修改 vec 中的内容,不返回
dedup
: 从一个 vec 中删除重复元素,保留第一个出现的元素(使用 HashSet)
fn add_n(v: Vec<i32>, n: i32) -> Vec<i32> {
// unimplemented!()
let mut ret_v: Vec<i32> = Vec::new();
for i in v.iter() {
ret_v.push(i + n);
}
return ret_v;
}
fn add_n_inplace(v: &mut Vec<i32>, n: i32) {
// unimplemented!()
for i in 0..v.len() {
v[i] += n;
}
}
fn dedup(v: &mut Vec<i32>) {
// unimplemented!()
let mut hs = HashSet::new();
let mut x = 0;
let len = v.len();
for _ in 0..len {
if hs.contains(&(v[x])) {
v.remove(x);
} else {
hs.insert(v[x]);
x += 1;
}
}
}
Part 3: hangman
写个猜词游戏的代码,直接放源码了,肯定有更好的写法,不过也没必要
// Simple Hangman Program
// User gets five incorrect guesses
// Word chosen randomly from words.txt
// Inspiration from: https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html
// This assignment will introduce you to some fundamental syntax in Rust:
// - variable declaration
// - string manipulation
// - conditional statements
// - loops
// - vectors
// - files
// - user input
// We've tried to limit/hide Rust's quirks since we'll discuss those details
// more in depth in the coming lectures.
extern crate rand;
use rand::Rng;
use std::fs;
use std::io;
use std::io::Write;
const NUM_INCORRECT_GUESSES: u32 = 5;
const WORDS_PATH: &str = "words.txt";
fn pick_a_random_word() -> String {
let file_string = fs::read_to_string(WORDS_PATH).expect("Unable to read file.");
let words: Vec<&str> = file_string.split('\n').collect();
String::from(words[rand::thread_rng().gen_range(0, words.len())].trim())
}
fn so_far_guess(word: Vec<char>, history_guessed: String) -> String {
let mut x = String::new();
for w in word {
if history_guessed.contains(w) {
x.push(w);
} else {
x.push('-');
}
}
return x;
}
fn guess_success(word: Vec<char>, history_guessed: String) -> bool {
for w in word {
if !history_guessed.contains(w) {
return false;
}
}
return true;
}
fn main() {
let secret_word = pick_a_random_word();
// Note: given what you know about Rust so far, it's easier to pull characters out of a
// vector than it is to pull them out of a string. You can get the ith character of
// secret_word by doing secret_word_chars[i].
let secret_word_chars: Vec<char> = secret_word.chars().collect();
// Uncomment for debugging:
println!("random word: {}", secret_word);
// Your code here! :)
let mut your_left_guesses = NUM_INCORRECT_GUESSES;
let mut your_guessed_letters = String::new();
while your_left_guesses > 0 {
println!("The word so far is {}", so_far_guess(secret_word_chars.clone(), your_guessed_letters.clone()));
println!("You have guessed the following letters: {}", your_guessed_letters);
println!("You have {} guesses left", your_left_guesses);
print!("Please guess a letter: ");
io::stdout()
.flush()
.expect("Error flushing stdout.");
let mut guess = String::new();
let mut cur_guess: char;
loop {
io::stdin()
.read_line(&mut guess)
.expect("Error reading line.");
if guess.len() != 0 {
cur_guess = guess.remove(0);
if !your_guessed_letters.contains(cur_guess) {
your_guessed_letters.push(cur_guess);
break;
}
}
}
if !secret_word.contains(cur_guess) {
your_left_guesses -= 1;
println!("Sorry, that letter is not in the word");
}
if guess_success(secret_word_chars.clone(), your_guessed_letters.clone()) {
break;
}
println!();
}
if your_left_guesses > 0 {
println!("Congratulations you guessed the secret word: {}!", secret_word);
} else {
println!("Sorry, you ran out of guesses!");
}
}