第一章 常见编程概念

可以到我我笔记原文中查看图片

http://note.youdao.com/noteshare?id=4133a57965ec0ed85d6047986c0fcdf9

第一 :变量和可变性

1、变量和可变性

变量默认是不可变的(immutable),这个是推动你以充分利用Rust提供安全性和简单并发性来编写代码的众多方式之一。

当变量不可变时,一旦值绑定一个名称上,就不能改变这个值

例子:

fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 8;
    println!("The value of x is: {}", x);
}

编译后的结果:

 

分析:

错误信息指出错误的原因是 不能对不可变变量 x 二次赋值(cannot assign twice to immutable variable x),因为你尝试对不可变变量 x 赋第二个值。

在尝试改变预设为不可变的值时,产生编译时错误是很重要的,因为这种情况可能导致 bug。

如果一部分代码假设一个值永远也不会改变,而另一部分代码改变了这个值,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 的起因难以跟踪,尤其是第二部分代码只是 有时 会改变值。

Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要追踪一个值如何和在哪可能会被改变,从而使得代码易于推导。

 

 

2、可变性

变量只是默认不可变,但是可变性也是非常有用的,我们如何才能让默认不可变变成可变呢?其实很简单,在变量名之前需要加一个 关键字

是 mut 。

如: let mut x=5;

fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 8;
    println!("The value of x is: {}", x);
}

编译后的结果:

 

分析:

通过 mut,允许把绑定到 x 的值从 5 改成 8。在一些情况下,你会想用可变变量,因为与只用不可变变量相比,它会让代码更容易编写。

除了防止出现 bug 外,还有很多地方需要权衡取舍。

例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解,为可读性而牺牲性能或许是值得的。

 

3、变量和常量的区别

不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:常量(constants)。类似于不可变变量,常量是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别:

a. 不允许对常量使用 mut。常量不光默认不能变,它总是不能变。

b. 声明常量使用 const 关键字而不是 let,并且 必须 注明值的类型。在下一部分,“数据类型” 中会介绍类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。

c. 常量可以在任何作用域中声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。

d. 常量只能被设置为常量表达式,而不能是函数调用的结果,或任何其他只能在运行时计算出的值。

 

例如:

const MAX_POINTS:u32=100_000;

在声明它的作用域之中,常量在整个程序生命周期中都有效,这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。

将遍布于应用程序中的硬编码值声明为常量,能帮助后来的代码维护人员了解值的意图。如果将来需要修改硬编码值,也只需修改汇聚于一处的硬编码值。

 

4、隐藏(Shadowing)

定义一个与之前变量同名的新变量,而新变量会 隐藏 之前的变量。Rustacean 们称之为第一个变量被第二个 隐藏 了,这意味着使用这个变量时会看到第二个值。可以用相同变量名称来隐藏一个变量,以及重复使用 let 关键字来多次隐藏,如下所示:

fn main() {
    let  x=5;
    let x=x+2;
    let x=x*5;
    println!("the value of x is:{}",x);
}

编译后的结果:

 

分析:

隐藏与将变量标记为 mut 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 let 关键字,就会导致编译时错误。通过使用 let,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。

 

第二:数据类型

在Rust中,每一个都属于某一个数据类型,这告诉Rust他被指定为何种数据,以便明确处理数据方式,两个数据类型子集:标量(scalar)和复合(compound)。

Rust 是静态数据类型语言,也就是说在编译时就必须知道所有变量的类型,根据值及其使用方式,编译器通常可以推断出我们想要的类型,当多种类型均有可能时,使用parse将string转换数字时,必须增加注解。

let guess: u32 = "42".parse().expect("Not a number!");

 

1、标量(scalar)

标量类型代表一个单独的值,Rust 有四种基本标量类型:整型,浮点型,布尔型和字符串型。

 

a.整型:没有小数部分的整数。该类型声明表明,它关联的值应该是一个占据 32 比特位的无符号整数(有符号整数类型以 i 开头而不是 u)。

长度

有符号

无符号

8-bit

i8

u8

16-bit

i16

u16

32-bit

i32

u32

64-bit

i64

u64

arch

isize

usize

每一个变体都可以是有符号或无符号的,并有一个明确的大小。有符号 和 无符号 代表数字能否为负值,换句话说,数字是否需要有一个符号(有符号数),或者永远为正而不需要符号(无符号数)

每一个有符号的变体可以储存包含从 -(2n - 1) 到 2n - 1 - 1 在内的数字,这里 n 是变体使用的位数。所以 i8 可以储存从 -(27) 到 27 - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从 0 到 2n - 1 的数字,所以 u8 可以储存从 0 到 28 - 1 的数字,也就是从 0 到 255。

另外,isize 和 usize 类型依赖运行程序的计算机架构:64 位架构上它们是 64 位的, 32 位架构上它们是 32 位的。

可以使用表格 3-2 中的任何一种形式编写数字字面值。注意除 byte 以外的所有数字字面值允许使用类型后缀,例如 57u8,同时也允许使用 _ 做为分隔符以方便读数,例如1_000。

表格 3-2: Rust 中的整型字面值

数字字面值

例子

Decimal

98_222

Hex

0xff

Octal

0o77

Binary

0b1111_0000

Byte (u8 only)

b'A'

那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常就很好,数字类型默认是 i32:它通常是最快的,甚至在 64 位系统上也是。isize 或 usize 主要作为某些集合的索引。

 

b. 浮点数

Rust 也有两个原生的 浮点数(floating-point numbers)类型,它们是带小数点的数字。Rust 的浮点数类型是 f32 和 f64,分别占 32 位和 64 位。默认类型是 f64,因为在现代 CPU 中,它与 f32 速度几乎一样,不过精度更高。

fn main() { let x = 2.0; // f64 let y: f32 = 3.0; // f32 }

 

c. 布尔型

fn main() { let t = true; let f: bool = false; // 显式指定类型注解 }

 

d.字符类型

fn main() { let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻'; }

 

2、复合类型

复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。

 

a.元组类型

元组是一个将多个其他类型的值组合进一个复合类型的主要方式。

fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1); }

tup 变量绑定到整个元组上,因为元组是一个单独的复合元素。为了从元组中获取单个值,可以使用模式匹配(pattern matching)来解构(destructure)元组值,像这样

fn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; println!("The value of y is: {}", y); }

 

a. 数组类型

与元组不同,数组中的每个元素类型必须相同,数组长度是固定长度,一旦声明,他们的长度不能增长和缩小。

 

第二:控制

a.控制流的方式总的有三种,(loop、if、while、for)

 

if 表达式允许根据执行条件不同,运行不同的代码块

fn main() {
    let number=3;
    if number<4{
        println!("已经超过number:{}",number);
    }else{
         println!("已经小于number:{}",number);
    }
}

编译结果:

 

备注:if 表达式必须是一个boolean值,否值会报错,

fn main() {
    let number=3;
    if number{ //这里会报错
        println!("已经超过number:{}",number);
    }else{
         println!("已经小于number:{}",number);
    }
}

编译结果:

 

使用 else if 处理多重条件

fn main() {
    // let number=3;
    // if number {
    //     println!("已经超过number:{}",number);
    // }else{
    //      println!("已经小于number:{}",number);
    // }
    //多重判断 else if
    let number=6;

    if number%4==0{
        println!("已经超过number4:{}",number%4);
    }else if number%3==0{
        println!("已经超过number3:{}",number%3);
    }else if number%2==0{
        println!("已经超过number2:{}",number%2);
    }else{
        println!("已经超过number1:");
    }

}

编译结果:

 

备注:当执行这个程序时,它按顺序检查每个 if 表达式并执行第一个条件为真的代码块。注意即使 6 可以被 2 整除,也不会输出 number is divisible by 2,更不会输出 else 块中的 number is not divisible by 4, 3, or 2。原因是 Rust 只会执行第一个条件为真的代码块,并且一旦它找到一个以后,甚至都不会检查剩下的条件了。

使用过多的 else if 表达式会使代码显得杂乱无章,所以如果有多于一个 else if 表达式,最好重构代码。为此,有一个Rust 分支结构(branching construct),叫做 match。

 

b. 在let中使用 if表达式

例如:

fn main(){
    let condition:bool=true;
    let _number=if condition{
        5
    }else{
        10
    };
    println!("已经超过number:{}",_number);
}

编译结果:

 

备注:记住,代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。在这个例子中,整个 if 表达式的值取决于哪个代码块被执行。这意味着 if 的每个分支的可能的返回值都必须是相同类型;在示例 3-2 中,if 分支和 else 分支的结果都是 i32 整型。如果它们的类型不匹配,如下面这个例子,则会出现一个错误:

fn main() {
    let condition = true;

    let number = if condition {
        5
    } else {
        "six"
    };

    println!("The value of number is: {}", number);
}

当编译这段代码时,会得到一个错误。if 和 else 分支的值类型是不相容的,同时 Rust 也准确地指出在程序中的何处发现的这个问题:

error[E0308]: if and else have incompatible types
 --> src/main.rs:4:18
  |
4 |       let number = if condition {
  |  __________________^
5 | |         5
6 | |     } else {
7 | |         "six"
8 | |     };
  | |_____^ expected integral variable, found &str
  |
  = note: expected type `{integer}`
             found type `&str`

第二:循环重复执行

 

Rust 为此提供了多种 循环(loops)。一个循环执行循环体中的代码直到结尾并紧接着回到开头继续执行。

 

a.使用loops重复执行代码

loops关键字 告诉Rust一遍一遍地执行一段代码直到明确要求停止

fn main() {     loop {     println!("again!");     } }

编译结果:

Compiling loops v0.1.0 (file:///projects/loops) Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs Running `target/debug/loops` again! again! again! again! ^Cagain!

备注:只有通过control+c才能停止,另外 ,Rust 提供了另一种更可靠的退出循环的方式。可以使用 break 关键字来告诉程序何时停止循环。

 

b. 从循环返回

loop 的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果传递给其它的代码。如果将返回值加入你用来停止循环的 break 表达式,它会被停止的循环返回:

fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("The result is {}", result); }

编译结果 The result is 20

 

while 循环

fn main() { let mut number = 3; while number != 0 { println!("{}!", number); number = number - 1; } println!("LIFTOFF!!!"); }

 

 

for 循环

fn main() { let a = [10, 20, 30, 40, 50]; for element in a.iter() { println!("the value is: {}", element); } }

 

fn main() { for number in (1..4).rev() { println!("{}!", number); } println!("LIFTOFF!!!"); }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值