来源:微信公众号【可回收BUG】
按照国际惯例,学习一门语言之前一定要亲自输出一句“Hello World”,这一个开启一扇新天地大门的神圣的仪式。
运行 cargo new hello_world
在生成的src/main.rs中输入:
fn main() {
println!("Hello World!");
}
运行cargo run
➜ learn_rust git:(master) ✗ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
Running `target/debug/learn_rust`
Hello World!
好,仪式结束,下面将进入Rust基本语法的正题。
一、格式化输出
在前面的例子内,直接输出了一个字面量字符串,上一期讲到类型时说到过,是一个引用类型&str,(时光机在这里《一起学Rust-变量及类型》),这里使用的println!是一个Rust宏,在Rust中结尾带有 ! 的均是宏,例如定义vector的vec!,格式化字符串format!,print!,eprintln!等。
使用宏的原因就是方便Rust编译器可以提前检查问题。说回来,下面看一下如何使用格式化输出:
let name = "他";
let grade = 3;
println!("{}已经{}年级了", name, grade);
-----输出-----
他已经3年级了
这里的name和grade属于位置参数,每一个 {} 需要匹配一个位置参数,而且这个数据类型是需要实现 std::fmt::Display trait,例如如果name是 Vec<T> 类型的则无法输出:
println!("{}", vec![1,2]);
-----输出-----
error[E0277]: `std::vec::Vec<{integer}>` doesn't implement `std::fmt::Display`
--> src/main.rs:13:20
|
13 | println!("{}", vec![1,2]);
| ^^^^^^^^^ `std::vec::Vec<{integer}>` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `std::vec::Vec<{integer}>`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required by `std::fmt::Display::fmt`
这种情况就要使用 {:?} 或者 {:#?} 来输出Debug信息,后者会输出带有格式的输出(例如换行、缩进)。
{} 只能按顺序匹配位置参数, {0} 在中间增加一个索引数字,则可以指定使用位置参数:
println!("{1}, {}, {0}, {}", "zero", "one");
-----输出-----
one, zero, zero, one
另外一种是带有名称的参数, {xxxx} 这样会可以使代码更具语义化,而且需要注意的是,位置参数不能位于名称参数之后:
//这样看起来
println!("{name}已经{grade}年级了", name="钢蛋", grade=3);
//println!("{name}已经{}年级了", name="钢蛋", 3); 这一句是无法编译的
println!("{name}已经{}年级了", 3, name="钢蛋");
格式化输出还有很多其他的模式:
:o 八进制
:x 十六进制(小写)
:X 十六进制(大写)
:p 指针
:b 二进制
:e 科学计数(小写)
:E 科学计数(大写)
:[num].[*|num] 数字占位/浮点精度格式化(四舍五入)
最后一种模式举例:
//四舍五入保留3位小数
println!("{n:.*}", 3, n=12.2456); //输出12.246
println!("{n:.3}", n=12.2456); //输出12.246
//数字总长为10,小数保留3位。
println!("{:10.3}", 12.2456); //输出 " 12.246"
二、条件控制
与其他语言相同, if , else if , else ,需要注意,只能接受布尔类型作为判断条件,而不能像是PHP中,可以直接使用任何一个变量值作为判断条件,语法举例如下:
let num = 10;
if num > 10 {
println!("大于10");
} else if num == 10 {
println!("等于10");
} else {
println!("小于10");
}
//下面是无法编译的,会抛出异常
let num = 1;
if num {
println!("true");
}
三、循环
loop 循环无条件,需要自行在循环体中加break,同样支持continue,而且支持通过break将循环内的数据返回给一个变量:
let mut num = 1;
let res = loop {
num += 1;
if num > 5 {
break num
}
};
println!("{}", res);
while 循环与大多数语言相同,终止条件同样必须使用布尔类型:
let mut num = 1;
while num < 3 {
num += 1;
}
println!("{}", num);
for...in... 循环可以对于遍历数组或者集合类的数据较为方便,用的较多,需要注意的是in后面只能接受一个可迭代的类型:
let arr = [1,2,3,4]; //数组未实现迭代器
for num in arr.iter() {
println!("{}", num);
}
遍历时,有些时候我们可能需要数字的索引键值,这时需要用到迭代器的一个方法 enumerate ,循环时可使用的值则是一个元组:
let nums = [1,2,3];
for (k, n) in nums.iter().enumerate() {
...
}
字符串的遍历:
let string = String::from("可回收BUG");
//按unicode字符遍历
for char in string.chars() {
print!("{}", char); //这里使用的非换行打印,输出“可回收BUG”
}
四、函数
函数定义使用 fn 定义,参数必须定义类型,返回值定义使用 -> 后方跟随返回类型,无返回值可不定义:
fn print_name(name:&str) { //无返回值
println!("{}", name);
}
fn add(a:i32, b:i32) -> i32 { //定义返回类型为i32
...
}
函数的返回有两种写法,普通的与其他语言相似,使用 return ,或者直接写一个表达式,代表返回:
fn main() {
let sum = add(2,3);
println!("{}", sum);
}
fn add(a:i32, b:i32) -> i32 {
a + b //代表返回a + b的结果,注意结尾没有分号,这是一个表达式
}
//下面是错误的,无法编译
fn add(a:i32, b:i32) -> i32 {
a + b; //分号结尾的是语句,而不是表达式
}
五、模式匹配
模式匹配的功能用处很大,内容也较多,这里先只介绍基本用法。
match 可以匹配多种情况,但是必须将全部情况列出,不过有的时候我们不需要关注所有情况,所以需要使用一个替代其他的情况,那就是 _ 下划线单独使用是特殊的存在,赋值给下划线标识,则代表后面的程序不会再使用它。看个例子:
//我们仅仅关注n=1时的结果,而不关心其他值的情况,
match n {
1 => {
println!("one");
},
_ => println!("other all");
}
//这里可以返回值并赋予一个变量,注意结尾是需要分号的。
let a = match n {
1 => n * 2,
_ => 0
};
match 的功能在例子中看起来和if差不多,但是它的能力远不止这些,这里仅做基本语法的探讨,后面在讲到枚举时还会讲到。
if let...else if let...else... 的使用,与 match 相同,需要列出全部,不需要关注的可以直接放到else内
let b = if let 2 = a {
a + 2
} else {
0
};
while let 则是使用模式匹配作为条件进行循环,停止则需要手动添加break,与while相同。
六、上期补充
补充一点变量定义的特性。
1. 变量覆盖
重复使用let对同一个变量名称进行声明并初始化值,后者会覆盖前者,并且可以赋予不同的数据类型,这意味着前面的变量不能再访问到。
let n = 1;
println!("{}", n);
let n = "string";
println!("{}", n);
2. 数字类型赋值
数字的定义可携带类型或者分隔符等,直接上例子:
let num1 = 2_i8;
let num2 = 10_0000i32;
let num3 = 1.2e5_f32;
let num4 = 1_e2;
println!("{},{},{},{}", num1, num2, num3, num4);
-----输出-----
2,100000,120000,100
可以直接在尾部跟随类型,为了阅读方便,也可以增加多个下划线,增加可读性,可以使用 e 来定义科学计数法的数字,默认是 f64 类型。
3. 数字的isize和usize
isize 与 usize 与cpu架构有关,64位的则它们的大小就是64位,32位同理。
4. 解构
可以将元组或数组中的值通过解构拆解出来,解构获取值的变量个数必须等于值的数量,不在意的变量可以使用下划线来获取,防止出现“未使用变量”的warning:
let [a, b] = [1,2];
let (a,_) = (3,4);
这期的分享就到这里啦,后续会增加小练习的项目分享与解析,觉得还不错的话就关注下吧~
完
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。