// variables1.rs
// Make me compile! Execute the command `rustlings hint variables1` if you want a hint :)
// About this `I AM NOT DONE` thing:
// We sometimes encourage you to keep trying things on a given exercise,
// even after you already figured it out. If you got everything working and
// feel ready for the next exercise, remove the `I AM NOT DONE` comment below.
fn main() {
//x = 5;
let x = 5;
println!("x has the value {}", x);
}
少了变量绑定。将let加上。
在Rust中允许先变量绑定后初始化,但是编译器会警告。这种做法一般也比较少的用。编译器禁止使用未经初始化的变量,因为这会产生未定义行为,当数据被相同的名称不变地绑定时,它还会冻结。在不可变绑定超出作用域之前,无法修改已冻结的数据。
Rust程序(大部分)由一系列语句构成。Rust有多种语句。最普遍的语句类型有两种:一种是绑定变量,另一种是表达式带上分号:
let x = 5; //变量绑定
//表达式
x;
x + 1;
15;
代码块也是表达式,所以它们在赋值操作中可以充当右值(r-values)。代码块中的最后一条表达式将赋给左值(l-value)。需要注意的是,如果代码块最后一条表达式结尾处有分号,那么返回值将变成 ()。即单元值。(代码块中的最后一条语句是代码块中实际执行的最后一条语句,而不一定是代码块中最后一行的语句。)
let x = 5u32;
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
// 将此表达式赋给 `y`
x_cube + x_squared + x
};
let z = {
// 分号结束了这个表达式,于是将 `()` 赋给 `z`
2 * x;
};
println!("x is {:?}", x);
println!("y is {:?}", y);
println!("z is {:?}", z);
输出:
x is 5
y is 155
z is ()
println!调用了一个Rust宏,并不是调用函数。若是调用函数,就是println。宏是后面的内容,这里不多说。相对于print!,println!自带换行,并将文本输出到控制台。编译器会检查格式化的正确性。
println!大体用法如下:
println!("Hello, world!");
println!("{} days", 31); //若不加后缀,31就自动成为i32类型
输出:
"Hello, world!"
31 days
变量代替字符串有多种写法
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); //位置参数
println!("{subject} {verb} {object}", //命名参数
object = "the lazy dog",
subject = "the quick brown fox",
verb = "jump over");
输出:
Alice, this is Bob. Bob, this is Alice
the quick brown fox jumps over the lazy dog
其他用法
println!("{} of {:b} people know binary, the other half don't", 1, 2); //类型指定
println!("{number:>width$}", number=1, width=6); //按指定宽度来右对齐文本
println!("{number:>0width$}", number=1, width=6); //可以在数字左边补0。语句输出"000001"
输出:
1 of 10 people know binary, the other half don't
1
000001
// variables2.rs
// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :)
fn main() {
//let x;
let x = 10;
if x == 10 {
println!("Ten!");
} else {
println!("Not ten!");
}
}
Rust中不允许只声明不赋值,Rust编译器会对代码作基本的静态分支流程分析,确保变量在使用之前一定被初始化。x没有绑定任何值,这样的代码会引起很多内存不安全的问题,比如计算结果非预期、程序崩溃,所以Rust编译器必须报错。
此处未对变量进行显式的类型声明,则整型默认为i32类型。若是浮点数则默认为i64类型。
后续会专门讲解if分支。
// variables3.rs
// Make me compile! Execute the command `rustlings hint variables3` if you want a hint :)
fn main() {
let mut x = 3;
println!("Number {}", x);
x = 5; // don't change this line
println!("Number {}", x);
}
let声明后的变量默认是不可变的。Rust是静态强类型语言,这就决定了在运行程序前Rust便需要知道所有变量的类型,而且不会自动将一个变量类型转为另一个类型。但是Rust可以根据上下文推断类型。
let mut inferred_type = 12; //根据下一行的赋值推断为i64类型
inferred_type = 4294967296i64;
扯远了。。我们想创建可修改的变量就加mut即可。mut修饰的变量具有可变性,mut修饰对应内存可以修改。所以上面x前加mut。
虽然mut的变量可变,但是类型不可变。下面的实例会报错。
let mut mutable = 12; // Mutable `i32`
mutable = 21; //不报错
//下面这句会报错,因为变量的类型并不能改变。
mutable = true;
但是我们可以通过变量遮蔽来修改类型,都是后话。
let mutable = true;
// variables4.rs
// Make me compile! Execute the command `rustlings hint variables4` if you want a hint :)
fn main() {
let x: i32;
println!("Number {}", x);
}
这个也是典型的光声明没初始化。不过通过这里我们可以提一下Rust类型的声明。
数字可以通过后缀或默认方式来声明类型,如下:
let logical: bool = true;
let a_float: f64 = 1.0; //常规说明
let an_integer = 5i32; //后缀说明
//否则会按默认方式决定类型。
let default_float = 3.0; //f64
let default_integer = 7; //i32
Rust提供多种原生类型,包括 标量类型
复合类型
之后会慢慢接触到。
// variables5.rs
// Make me compile! Execute the command `rustlings hint variables5` if you want a hint :)
fn main() {
let number = "T-H-R-E-E"; // don't change this line
println!("Spell a Number : {}", number);
number = 3;
println!("Number plus two is : {}", number + 2);
}
这里即是考察了前文所述的变量遮蔽。我们无法对number进行修改,就算加mux,我们也不能将number从String类型变为i32类型。所以这里采用变量遮蔽的方式。
fn main() {
let number = "T-H-R-E-E";
println!("Spell a Number : {}", number);
let number :i32 = 5;
println!("Number plus two is : {}", number + 2);
}
// variables6.rs
// Make me compile! Execute the command `rustlings hint variables6` if you want a hint :)
const NUMBER = 3;
fn main() {
println!("Number {}", NUMBER);
}
这里简单考察了const。变量可以给出类型说明,有常规说明和后缀说明,在没有给出说明的情况就按默认方式决定类型(let)。
但const必须指定变量类型,不能省略。而且const命名方式倾向于全部大写,否则编译器会给出警告。
Rust有两种常量,可以在任意作用域声明,包括全局作用域。它们都需要显式的类型声明:
有个特例就是string字面量。它可以不经改动就被赋给一个static变量,因为它的类型标记:&’static str就包含了所要求的生命周期’static。其他的引用类型都必须特地声明,使之拥有’static 生命周期。这两种引用类型的差异似乎也无关紧要,因为无论如何,static 变量都得显式地声明。
示例:
//全局变量是在在所有其他作用域之外声明的。
static LANGUAGE: &'static str = "Rust";
const THRESHOLD: i32 = 10;
fn is_big(n: i32) -> bool {
// 在一般函数中访问常量
n > THRESHOLD
}
fn main() {
let n = 16;
// 在 main 函数(主函数)中访问常量
println!("This is {}", LANGUAGE);
println!("The threshold is {}", THRESHOLD);
println!("{} is {}", n, if is_big(n) { "big" } else { "small" });
}