创建第一个rust程序 - 变量,数据类型

创建并使用变量

开发者用数据写电脑程序.数据可以被聚合,分析,存储,处理,分享和出报表.用变量来存命名引用来我们的数据,在后边代码会用到.

变量

在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大小和有符号的签名来定义的.一个有符号的整形可以是正或负数.无符号整形只能 为正数.

LengthSignedUnsigned
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
architecture-dependentisizeusize

isizeusize类型依赖于程序运行计算机类型.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类型有两个可能的值:truefalse.布尔值被广泛用在表达式判断中.如果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 slicestring数量一个视图. 大多数时候我们用这些类型时是通过引用风格的语法,在类型前加一个&. 下下边模块中会有这样引用.从现在起,你可以认为&str是不变string类型的一个指针. 字符串字面量都是&str类型.

虽然字符串字面量在介绍的rust例子中使用非常方便,它们并不适合在那些我们想要用文本的所有场景. 不是每个string可以在编译时就被知道的.一个例子是当用户与程序交互,在运行期间通过终端发一个文本.

对于这些场景,rust有第二个字符串类型String.这个类型被分配在堆上.当你使用String类型时,string(字符数量)在编译时是不被知道的.
注意:如果你熟悉垃圾回收语言,你可能想知道为什么Rust有两类字符串类型. Strongs是极为复杂的数据类型.大多数语言使用它们的垃圾回收器来掩盖它的复杂性.Rust是一个系统语言,暴露出一些strings固有的复杂性. 随着
复杂性增加,在你的程序中内存怎样被控制越来越变量更细粒度.

事实上,rust有不至两个字符串类型.在本模块中,我们仅包含了String&str. 学习更多字符串类型可以在Rust文档.
在没有学习Rust ownershipborrowing system之前,我们不能把所有的Stringstr的不同列出来. 到那时,你可以知道 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中每个元素的索引

ElementValueData type
0Echar
15i32
2truebool

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)方法.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值