目录
创建并使用变量
开发者用数据写电脑程序.数据可以被聚合,分析,存储,处理,分享和出报表.用变量来存命名引用来我们的数据,在后边代码会用到.
变量
在Rust中,用关键词let来声明变量.每个变量有一个唯一的名字.在程序中当变量被声名了,它被绑定一个值,或是这个值在过后再绑定.下边代码声明一个名为a_number的变量.
let a_number;
变量a_number还没有绑定值.我位可以修改现在语句来绑定值.
let a_number=10;
看下其它例子.下边代码有两个变量.第一个声明并绑定一个数值.第二个声明,但没有绑定值.在过后的程序中,这个变量会绑定一个单词.代码中调用println!
宏来展示变量值
// Declare a variable
let a_number;
// Declare a second variable and bind the value
let a_word = "Ten";
// Bind a value to the first variable
a_number = 10;
println!("The number is {}.", a_number);
println!("The word is {}.", a_word);
输出
The number is 10.
The word is Ten.
如果调用println!
宏,尝试展示a_number
没有绑定的值,编译器会返回错误.查看错误消息在Rust Playgroud.
对比不变与可变
在rust中默认绑定为不可变.当变量不可变,值绑定到这个变量上时,你不能再改变这个值.
例如,如果我们在上个例子中试着改变a_number
的值,会从编译器接收到错误消息.
// Change the value of an immutable variable
a_number = 15;
可以在Rust Playground上做这样改变测试.
对于可变值,我们要在开始时用mut关键词使变量绑定为可变的.
// The `mut` keyword lets the variable be changed
let mut a_number = 10;
println!("The number is {}.", a_number);
// Change the value of an immutable variable
a_number = 15;
println!("Now the number is {}.", a_number);
输出
The number is 10.
Now the number is 15.
编译器中代码编译没有错误,因为a_number
现在是可变变量.
变量遮蔽
你可以声明一个新变量,使用已存在相同的变量名.这个新声明创建并新绑定.在Rust中这个操作被称为"遮蔽",因为新变量遮蔽前一个变量.老的变量仍然存在,但是不能在其它作用域引用.
下面代码展示使用遮蔽.定义变量名为shadow_num. 我们没有定义这个变量为可变的,因为每个let操作都创建一个新的number当使用遮蔽绑定.
// Declare first variable binding with name "shadow_num"
let shadow_num = 5;
// Declare second variable binding, shadows existing variable "shadow_num"
let shadow_num = shadow_num + 5;
// Declare third variable binding, shadows second binding of variable "shadow_num"
let shadow_num = shadow_num * 2;
println!("The number is {}.", shadow_num);
探索数值,文本和布尔数据类型
Rust是静态类型语言.编译器在你代码编译和运行时必需知道精确知道变量的类型.编译器可以根据绑定值来推断类型.你不总是需要显示在你的代码中定义类型.当可能是很多类型时,你必须要告诉编译器是什么类型通过使用类型注释.
下下边例子中,告诉编译器创建一个变量number为32位整形.在变量名后指定数数类型为u32.注意需要在变量名后用:
.
let number: u32 = 14;
println!("The number is {}.", number);
如果变量值用双引号标记,编译器会推断值是文本类型而不是数值类型.变量值推断没有和u32
匹配上,所以编译器发出错误提示
let number: u32 = "14";
输出
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:2:23
|
2 | let number: u32 = "14";
| --- ^^^^ expected `u32`, found `&str`
| |
| expected due to this
error: aborting due to previous error
内置数据类型
Rust内置一些数据类型来表示数值,文本和布尔. 不同的数据类型作为标题参考,因为他们表示单独一个值.
- 整形
- 浮点型
- 布尔
- 字符
rust也提供更多复杂类型与数据序列一起,像string和tuple.
数值类型: 整型和浮点型
在Rust中整形是通过bit大小和有符号的签名来定义的.一个有符号的整形可以是正或负数.无符号整形只能 为正数.
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
architecture-dependent | isize | usize |
isize
和usize
类型依赖于程序运行计算机类型.64bit架构的为64bit,32bit架构是32bit的.如果你不指定整数类型,系统不能推断出类型,默认被分区为i32(32bit签名整数).
Rust为十进制提供两个浮点数据类型,f32(32bit)和f64(64bit).默认浮点数据类型为f64.在现代CPU上,f64数据和f32相同,但是它更精确.
let number_64 = 4.0; // 编译器推断值使用默认64bit
let number_32: f32 = 5.0; // 通过声明指定32bit
在Rust中所有基础的数值类型都支持数学运算,像加,减,乘,除.
// Addition, Subtraction, and Multiplication
println!("1 + 2 = {} and 8 - 5 = {} and 15 * 3 = {}", 1u32 + 2, 8i32 - 5, 15 * 3);
// Integer and Floating point division
println!("9 / 2 = {} but 9.0 / 2.0 = {}", 9u32 / 2, 9.0 / 2.0);
布尔类型:True 或False
Rust中布尔类型被用来存储真伪.bool
类型有两个可能的值:true
或false
.布尔值被广泛用在表达式判断中.如果bool
语句或值为true时,向下执行,否则(语句或值为false)做不同的行为.布尔值通常通过对比检查返回.
在下边例子中,使用大于号>
来测试两个值.这个操作返回布尔值.
// Declare variable to store result of "greater than" test, Is 1 > 4? -- false
let is_bigger = 1 > 4;
println!("Is 1 > 4? {}", is_bigger);
文本类型:Characters和Strings
Rust支持文本类型用两个基本类型String类型和charactor类型. character是文本类型中一个字符,而string是一系列characters. 所有文本类型用UTF-8来表示.
char
类型是文本类型中最基础的.值被单引号包围
let uppercase_s = 'S';
let lowercase_f = 'f';
let smiley_face = '😃';
注意:一些语言它的char
类型是8bit无符号整数,等同于Rust中u8类型. 在Rust中char
类型包含unicode代码点,并且它们没有 使用utf8编码. Rust中char
是一个21bit整形, 填充32bit宽度. char
直接包含纯代码点值.
String
str
类型也就是string slice
是string
数量一个视图. 大多数时候我们用这些类型时是通过引用风格的语法,在类型前加一个&
. 下下边模块中会有这样引用.从现在起,你可以认为&str
是不变string
类型的一个指针. 字符串字面量都是&str
类型.
虽然字符串字面量在介绍的rust例子中使用非常方便,它们并不适合在那些我们想要用文本的所有场景. 不是每个string
可以在编译时就被知道的.一个例子是当用户与程序交互,在运行期间通过终端发一个文本.
对于这些场景,rust有第二个字符串类型String
.这个类型被分配在堆上.当你使用String
类型时,string(字符数量)在编译时是不被知道的.
注意:如果你熟悉垃圾回收语言,你可能想知道为什么Rust有两类字符串类型. Strongs
是极为复杂的数据类型.大多数语言使用它们的垃圾回收器来掩盖它的复杂性.Rust是一个系统语言,暴露出一些strings固有的复杂性. 随着
复杂性增加,在你的程序中内存怎样被控制越来越变量更细粒度.
事实上,rust有不至两个字符串类型.在本模块中,我们仅包含了String
和&str
. 学习更多字符串类型可以在Rust文档.
在没有学习Rust ownership
和borrowing system
之前,我们不能把所有的String
和str
的不同列出来. 到那时,你可以知道 String
数据类型,在你程序运行时可以改变.&str
引用是不变文本数据的视图,在你程序运行时不能改变.
文本例子
下边的例子展示了在Rust中怎样使用char
和&str
数据类型.
- 两个字符串变量是被声明用
:char
注解语法.值被指定使用单引号. - 第3个字符串变量声明绑定一个图片.对于这个变量,我们让编译器推断出库他的数据类型.
- 两个字符串变量声明通过它们各自的值.字符串是用双引号包裹起来的.
- 一个字符串声明有
: & str
注释来指定数据类型. 其它变量左侧数据类型是没有指定.编译器推断数据是基于上正文.
注意string_1
变量包含一个空格在最后字符序列的最后.
// Specify the data type "char"
let character_1: char = 'S';
let character_2: char = 'f';
// Compiler interprets a single item in quotations as the "char" data type
let smiley_face = '😃';
// Compiler interprets a series of items in quotations as a "str" data type and creates a "&str" reference
let string_1 = "miley ";
// Specify the data type "str" with the reference syntax "&str"
let string_2: &str = "ace";
println!("{} is a {}{}{}{}.", smiley_face, character_1, string_1, character_2, string_2);
输出为
😃 is a Smiley face.
使用tuple和struct定义数据集合
在本单元我们探索数据类型,它是非常有用对于数据集合或复杂类型:tuple和struct.
tuple
tuple是一组不同类型聚合到一起的值.在tuple中每个独立的值叫元素.值之前用,
做分隔,外边被小括号围着(<value1>,<value2>,..)
.
tuple有固定长度,长度和元素数量相等.当tuple声明完,它不能增加或缩减长度.元素不能增加或移除.tuple被定义通过一连串类型的元素.
定义tuple
这个例子是一个tuple用到3个元素:
// Tuple of length 3
let tuple_e = ('E', 5i32, true);
下边表展示了值,数据类型,和在tuple中每个元素的索引
Element | Value | Data type |
---|---|---|
0 | E | char |
1 | 5 | i32 |
2 | true | bool |
tuple的类型标签定义通过一连串两个元素类型(char, i32, bool)
访问tuple中元素
tuple中元素可以通过起始索引为0来访问.这个过程被称为tuple索引. 为了访问tuple中元素,我们使用语法<tuple>.<index>
.
下边例子展示通过索引来访问tuple中元素.
// Declare a tuple of three elements
let tuple_e = ('E', 5i32, true);
// Use tuple indexing and show the values of the elements in the tuple
println!("Is '{}' the {}th letter of the alphabet? {}", tuple_e.0, tuple_e.1, tuple_e.2);
输出
Is 'E' the 5th letter of the alphabet? true
Tuple被使用是当你想要组合不同类型到一个值时.函数可以使用tuple返回多个值,因为tuple可以保存多个值.
struct
struc是组合其它类型的类型.在struct中元素被称为字段.像tuple,在struct中字段可以有不同类型. struct类型非常好的优点是你可以为不同字段命名,这样他们的意思非常清楚.
在rust中使用struct,首先你先定义结构名,并且为不同字段指定数据类型.然后你用另外名创建struct实例.当你声明实例时,你为字段提供一个特殊的值.
rust提供三个结构类型:class struct, tuple struct和unit struct.这个struct支持不同方式对数据分组和处理方式.
- Classic C structs 是最常用的.每个在struct中字段 有名字和类型.定义classic struct之后,struct中的字段可以通过语法
<struct>.<field>
被访问 - Tuple structs 和classic structs类似,但是字段没有名字.访问字段通过 tuple struct,我们使用和tuple相同的语法
<tuple>.<index>
.和tuple一样,索引也是从0开始. - Unit structs 通常作为一个标记.当我们学习到rust
traits
特性时,我们会了解更多为什么会使用 unit structs .
下边代码展示了定义3种struct类型
// Classic struct with named fields
struct Student { name: String, level: u8, remote: bool }
// Tuple struct with data types only
struct Grades(char, char, char, char, f32);
// Unit struct
struct Unit;
定义struct
为定义struct,我们输入关键字struct
后跟struct名字.为struct 选择一个名字来描述这组数据的显著特征. 不同之前的命名习惯,struct命名是驼峰的.
在rust中struct类型通常在main函数和其它函数外定义.由于这个原因,开始定义struct左边不需要缩进.仅里边部分定义需要缩进来展示数据的组织.
Classic struct
像函数一样,classic struct体是定义在{}
里.每个classic struct中字段给一个在struct中唯一的名字.每个字段类型指定语法为: <type>
. classic struct中字段被,
分隔<field>,<field>,..
. classic struct定义结尾不需要用;
.
// Classic struct with named fields
struct Student { name: String, level: u8, remote: bool }
得益于classic struct定义,你可以访问字段通过字段名字.为了访问字段值,可以使用语法<struct>.<field>
.
Tuple struct
像tuple一样,tuple struct体定义里边为()
.小括号直接跟着struct 名字.没有空格在小括号和名字之间.
不同tuple,tuple struct定义包含仅仅是每个字段类型.在typle中的数据类型规定一个带逗号分隔的列表<type>, <type>, ...
.
// Tuple struct with data types only
struct Grades(char, char, char, char, f32);
实例化struct
当你定义struct类型之后 ,你使用结构通过创建一个实例类型,为每个字段指定值.当你设置字段值时,你不需要以你定义的序列指定字段值.
下边例子使用定义struct,我们创建 Strudent和Grades struct类型.
// Instantiate classic struct, specify fields in random order, or in specified order
let user_1 = Student { name: String::from("Constance Sharma"), remote: true, level: 2 };
let user_2 = Student { name: String::from("Dyson Tan"), level: 5, remote: false };
// Instantiate tuple structs, pass values in same order as types defined
let mark_1 = Grades('A', 'A', 'B', 'A', 3.75);
let mark_2 = Grades('B', 'A', 'A', 'C', 3.25);
println!("{}, level {}. Remote: {}. Grades: {}, {}, {}, {}. Average: {}",
user_1.name, user_1.level, user_1.remote, mark_1.0, mark_1.1, mark_1.2, mark_1.3, mark_1.4);
println!("{}, level {}. Remote: {}. Grades: {}, {}, {}, {}. Average: {}",
user_2.name, user_2.level, user_2.remote, mark_2.0, mark_2.1, mark_2.2, mark_2.3, mark_2.4);
转换string literal 为String类型
String数据存储内部另外的数据结构,像struct或vector,一定转换从string literal引用(&str
)到String
类型.为做这样转换,我们使用标准的String::from(&str)
方法.注意我们在这个例子怎样使用这样方法
// Classic struct with named fields
struct Student { name: String, level: u8, remote: bool }
...
let user_2 = Student { name: String::from("Dyson Tan"), level: 5, remote: false };
如果我们在分配的值不转换类型,编译器会发布这样错误
error[E0308]: mismatched types
--> src/main.rs:24:15
|
24 | name: "Dyson Tan",
| ^^^^^^^^^^^
| |
| expected struct `String`, found `&str`
| help: try using a conversion method: `"Dyson Tan".to_string()`
error: aborting due to previous error
编译器建议我们使用.to_string()
函数来转换.在我们例子中使用的是String::from(&str)
方法.