专栏首页江湖安得便相忘一起学Rust-基本语法

一起学Rust-基本语法

来源:微信公众号【可回收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中输入:

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);

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一起学Rust-引用 · 借用

    接续上一期的所有权的学习,所有权的内容中强调的是变量是资源的所有者,拥有对资源的控制权(例如移动,释放),但并不是所有的变量都拥有所指向的资源,那就是引用(Re...

    江湖安得便相忘
  • 一起学Rust-结构体

    最近一段时间没有坚持写学习记录,总是给自己加班很晚,工作很忙这些理由来自我安慰,回想来属实有些羞愧,总是暗示自己没有时间,但是真正空闲的时间却在我一边安慰自己一...

    江湖安得便相忘
  • 一起学Rust-实战leetcode(三)

    之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。

    江湖安得便相忘
  • SSM示例项目

    项目使用Spring4.1.2.RELEASE + SpringMVC4.1.2.RELEASE + Mybatis3.3.0

    凯哥Java
  • Android截屏的几种实现

    此种方式比较简单只需传入当前要截取屏幕的Activity对象即可,不需要添加任何权限,后续可将截图的bitmap保存到本地即可;

    IT大飞说
  • LeetCode 38.Count and Say

    c++ ``` class Solution { public: string a[31]; string countAndSay(int n) {

    ShenduCC
  • spring集成activemq 原

    两个项目,一个生产者一个消费者,这里只贴出关键代码(队列模式和订阅模式),文章最后会附上项目地址,有需要的可以自行下载。项目访问地址http://localho...

    尚浩宇
  • 案例解析:海底捞、银行、商务酒店、香水单品市场、运营和销售数据挖掘

    沉默的白面书生
  • 快速学习-Shell概述

    大数据程序员为什么要学习Shell呢? 1)需要看懂运维人员编写的Shell程序。 2)偶尔会编写一些简单Shell程序来管理集群、提高开发效率。.

    cwl_java
  • Linux的“壳”

    在上一篇文章中,我们已经初尝了Shell的好处。由于我们后面将大量借助Shell,所以在这里先简要介绍一下这件工具。 什么是Shell 我们已经说过,Shell...

    Vamei

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动