前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一起学Rust-基本语法

一起学Rust-基本语法

作者头像
江湖安得便相忘
修改2019-08-26 11:07:13
1.8K0
修改2019-08-26 11:07:13
举报

来源:微信公众号【可回收BUG】

原文链接:https://mp.weixin.qq.com/s?__biz=MzIyODE1NDIyOQ==&mid=2247483750&idx=1&sn=807faf62137a36c992480264841ceb0a&chksm=e8570f7cdf20866a320858c6c07fd3b2c312899a4223ab8447fdc8b39ef0b55a5cc337213376&token=609429862&lang=zh_CN#rd

按照国际惯例,学习一门语言之前一定要亲自输出一句“Hello World”,这一个开启一扇新天地大门的神圣的仪式。

运行  cargo new hello_world  在生成的src/main.rs中输入:

代码语言:javascript
复制
fn main() {
    println!("Hello World!");
}

运行cargo run

代码语言:javascript
复制
➜  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编译器可以提前检查问题。说回来,下面看一下如何使用格式化输出:

代码语言:javascript
复制
let name = "他";
let grade = 3;
println!("{}已经{}年级了", name, grade);

-----输出-----

他已经3年级了

这里的name和grade属于位置参数,每一个  {}  需要匹配一个位置参数,而且这个数据类型是需要实现   std::fmt::Display  trait,例如如果name是  Vec<T>  类型的则无法输出:

代码语言:javascript
复制
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}   在中间增加一个索引数字,则可以指定使用位置参数:

代码语言:javascript
复制
println!("{1}, {}, {0}, {}", "zero", "one");

-----输出-----

one, zero, zero, one

另外一种是带有名称的参数,  {xxxx}   这样会可以使代码更具语义化,而且需要注意的是,位置参数不能位于名称参数之后:

代码语言:javascript
复制
//这样看起来
println!("{name}已经{grade}年级了", name="钢蛋", grade=3);


//println!("{name}已经{}年级了", name="钢蛋", 3); 这一句是无法编译的
println!("{name}已经{}年级了", 3, name="钢蛋");

格式化输出还有很多其他的模式:

代码语言:javascript
复制
:o  八进制
:x  十六进制(小写)
:X  十六进制(大写)
:p  指针
:b  二进制
:e  科学计数(小写)
:E  科学计数(大写)
:[num].[*|num]  数字占位/浮点精度格式化(四舍五入)

最后一种模式举例:

代码语言:javascript
复制

//四舍五入保留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中,可以直接使用任何一个变量值作为判断条件,语法举例如下:

代码语言:javascript
复制
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将循环内的数据返回给一个变量:

代码语言:javascript
复制
let mut num = 1;
let res = loop {
    num += 1;
    if num > 5 {
        break num
    }
};

println!("{}", res);

  while   循环与大多数语言相同,终止条件同样必须使用布尔类型:

代码语言:javascript
复制
let mut num = 1;
while num < 3 {
    num += 1;
}
println!("{}", num);

  for...in...   循环可以对于遍历数组或者集合类的数据较为方便,用的较多,需要注意的是in后面只能接受一个可迭代的类型:

代码语言:javascript
复制

let arr = [1,2,3,4];  //数组未实现迭代器
for num in arr.iter() {
    println!("{}", num);
}

遍历时,有些时候我们可能需要数字的索引键值,这时需要用到迭代器的一个方法  enumerate   ,循环时可使用的值则是一个元组:

代码语言:javascript
复制
let nums = [1,2,3];
for (k, n) in nums.iter().enumerate() {
    ...
}

字符串的遍历:

代码语言:javascript
复制
let string = String::from("可回收BUG");
//按unicode字符遍历
for char in string.chars() {
    print!("{}", char);  //这里使用的非换行打印,输出“可回收BUG”
}

四、函数

函数定义使用   fn   定义,参数必须定义类型,返回值定义使用   ->   后方跟随返回类型,无返回值可不定义:

代码语言:javascript
复制
fn print_name(name:&str) {  //无返回值
    println!("{}", name);
}

fn add(a:i32, b:i32) -> i32 {  //定义返回类型为i32
    ...
}

函数的返回有两种写法,普通的与其他语言相似,使用   return   ,或者直接写一个表达式,代表返回:

代码语言:javascript
复制
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   可以匹配多种情况,但是必须将全部情况列出,不过有的时候我们不需要关注所有情况,所以需要使用一个替代其他的情况,那就是   _   下划线单独使用是特殊的存在,赋值给下划线标识,则代表后面的程序不会再使用它。看个例子:

代码语言:javascript
复制
//我们仅仅关注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内

代码语言:javascript
复制
let b = if let 2 = a {
    a + 2
} else {
    0
};

  while let   则是使用模式匹配作为条件进行循环,停止则需要手动添加break,与while相同。

六、上期补充

补充一点变量定义的特性。

1. 变量覆盖

重复使用let对同一个变量名称进行声明并初始化值,后者会覆盖前者,并且可以赋予不同的数据类型,这意味着前面的变量不能再访问到。

代码语言:javascript
复制
let n = 1;
println!("{}", n);
let n = "string";
println!("{}", n);

2. 数字类型赋值

数字的定义可携带类型或者分隔符等,直接上例子:

代码语言:javascript
复制
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:

代码语言:javascript
复制
let [a, b] = [1,2];
let (a,_) = (3,4);

这期的分享就到这里啦,后续会增加小练习的项目分享与解析,觉得还不错的话就关注下吧~

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档