专栏简介:本专栏作为Rust语言的入门级的文章,目的是为了分享关于Rust语言的编程技巧和知识。对于Rust语言,虽然历史没有C++、和python历史悠远,但是它的优点可以说是非常的多,既继承了C++运行速度,还拥有了Java的内存管理,就我个人来说,还有一个优点就是集成化的编译工具cargo,语句风格和C++极其相似,所以说我本人还是比较喜欢这个语言,特此建立这个专栏,作为学习的记录分享。
日常分享:每天努力一点,不为别的,只是为了日后,能够多一些选择,选择舒心的日子,选择自己喜欢的人!
目录
Rust中的变量
可变变量
不可变变量
常量
隐藏
数据类型
标量类型
整型
浮点型
布尔型
字符类型
复合类型
元组类型
数组类型
总结
相信熟悉c++或者其他编程语言的,都会很熟悉变量这个词,变量作为暂时存储数值的容器,不过在Rust中,变量默认是不可变的。这个特性和Rust的性质是有关系的。虽然说是叫变量,但是又是不可变的,这是由于一个所有权的原因,这个问题留到后面细细讲解。
变量的声明关键字是:let
如果是要声明可变变量,则添加关键字mut.
fn main() {
let mut rng = 10;
println!("rng的为: {}", rng);
rng=20;
println!("rng的为: {}", rng);
}
可以看到这里变量rng的值可以再次赋值,不会出现报错。
不可变变量的定义就是可变变量没有mut关键字,不能二次赋值。
我们发现这里出现了报错,不允许二次对变量rcg赋值,这代表就是不可变变量。
类似于不可变变量,常量 (constants) 是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
首先,不允许对常量使用 mut
。常量不光默认不可变,它总是不可变。声明常量使用 const
关键字而不是 let
,并且 必须 注明值的类型。在下一部分,“数据类型” 中会介绍类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。
常量可以在任何作用域中声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。
最后一个区别是,常量只能被设置为常量表达式,而不可以是其他任何只能在运行时计算出的值。这里和c++中的常量表达式十分相似。其实简单的说就是常量在编译时就必须获得值,而不是在运行过程中才能获得值。下面举几个例子:
const THETA_COUNT: usize =30*1024;
println!("THETA_COUNT: {}", THETA_COUNT);
这里定义了一个常量,是30*1024,结果运行成功,这是因为在编译时就已经计算出了确切的值,我们再看另一个例子:
fn main() {
const THETA_COUNT2:usize=get_value_count();
println!("THETA_COUNT2: {}", THETA_COUNT2);
}
fn get_value_count() -> usize {
let count =30;
return count;
}
error[E0015]: cannot call non-const fn `get_value_count` in constants
最后结果报错,显示不能由函数转化成常量。这就是常量的特性,只能由在编译时能获的确切值的表达式组成。
前面提到的可变变量,虽然可以二次赋值,但是需要声明是可变变量,除此之外,Rust还有一种方式可以改变变量的值——隐藏。
我们可以定义一个与之前变量同名的新变量。Rustacean 们称之为第一个变量被第二个 隐藏(Shadowing) 了,这意味着当您使用变量的名称时,编译器将看到第二个变量。实际上,第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 let
关键字来多次隐藏。
fn main() {
let args=10;
println!("args: {}", args);
let args=args*args;
println!("args: {}", args);
{
let args=20;
println!("args: {}", args);
}
println!("args: {}", args);
}
这里先是将args绑定到10上,接着通过关键字let创建一个新变量args,args的值就变成100,然后在花括号创建的域内,let关键字将args创建一个新变量,赋值为20,第三个let只在域内起作用,花括号之外的的值依旧是原本的值,运行的结果:
Finished dev [unoptimized + debuginfo] target(s) in 0.29s
Running `target/debug/Rust`
args: 10
args: 100
args: 20
args: 100
看到这里,因该有小伙伴发现隐藏和可变变量似乎一样,其实还是存在差异,隐藏是创建了一个新变量,而可变变量的数据类型是不能变得,只能由定义时的数据类型决定。
fn main() {
let str="hello";
let length=str.len();
println!("length is {}",length);
let mut buffer="Hello, world!";
buffer=buffer.len()+2;
println!("buffer is {}",buffer);
}
这里显示报错,所以说隐藏和可变变量的区别就是数据类型是否可以变化。
在 Rust 中,每一个值都属于某一个 数据类型(data type),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。
Rust语言是一种静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的内型,这点和python不一样,python是动态类型语言,其内使用的所有变量不需要指明数据类型。但是一般来说,编译器是可以推断出变量的数据类型,但是如果数据类型转换过于复杂,那就需要你去注释。
fn main(){ let sm=String::from("hello"); let num:usize=sm.parse().expect("not number"); println!("{}",num); }
这里的num变量的数据类型必须标注,如果不标注就无法编译通过,这也就再次证明了静态型语言的特点。
在高中物理中有提到过标量和矢量,当时的标量是指一个没有方向只有大小的值,但是,这里的标量指的是单独的一个变量。用官方语言来解释就是:标量(scalar)类型代表一个单独的值。。
Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。
整数是一个没有小数的数字。他分为无符号整型和有符号整型。
长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 |
u8 |
16-bit | i16 |
u16 |
32-bit | i32 |
u32 |
64-bit | i64 |
u64 |
128-bit | i128 |
u128 |
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 位的。
Rust的默认类型为i32,当你不知道使用什么类型的时候,就是用默认的数据类型。
和其他语言也一样,Rust也有浮点数,只不过不像c++那样分为float和double两种类型,但是也是由双精度(f64)和单精度(f32)两种类型组成。Rust默认的是64位。
fn main() {
let x=45.6;
let y:f32=44.6;
println!("x:{},y:{}",x,y);
}
浮点数采用 IEEE-754 标准表示。f32
是单精度浮点数,f64
是双精度浮点数。
和C++语言一样,Rust也有布尔类型,true,false。数值类型是bool.
fn main() {
let flag=true;
let flags:bool = false;
}
布尔类型主要场景还是用在控制流方面。作为一个判断条件。
Rust语言的char类型和其他语言一样,但是优点就是通用性比较好,就连一些表情都是可以进行存储的。
fn main() {
let c='c';
println!("c is {}",c);
let a='';
println!("a is {}",a);
}
Rust语言的cahr类型的大小为四个字节,并代表一个Unicode标量值。比其他语言使用ASCII表示的内容多。所以这也是Rust的一个优点。
复合类型就是指多个值合成一个复合类型。Rust中有元组和数组两种符合类型。
学习过python的应该都知道,元组是一种将多个其他类型的值组合成一个复合类型的主要方式。元组的特性就是长度不能改变,一旦定义,就不能再更改元组的长度。注意,这里的元组和Python中的元组定义不一样。这里只是说元组的长度不变。
fn main() {
let tup:(f32,f64,i32)=(0.9,3.42,20);
}
tup变量绑定到在整个元组上, 元组是一个复合函数,我们可以用模式匹配来进行获得元组中的值,模式匹配后面会更详细的介绍,这里就只是使用。
fn main() {
let mut tup:(f32,f64,i32)=(0.9,3.42,20);
//tup.1=0.8;
let (x,y,z)=tup;
println!("{} {} {}",x,y,z);
}
这里使用的方法是解构方法。
除了解构方法,还有一种方式就是索引。不过和其他语言不同的是,不再需要[]号来存放下标。
fn main() {
let tup:(i32,f32,usize) =(32,34.8,89);
println!("The first number is {}",tup.0);
println!("The second number is {}",tup.1);
println!("The thrid number is {}",tup.2);
}
上面说的,元组的值可以改变,不过只有定义元组为可变变量时,才能够改变元组的值。这里我来举个例子。
fn main() {
let mut rng:(i32,i64,isize)=(23,34,90);
rng.1=30;
println!("{},{},{}",rng.0,rng.1,rng.2);
}
注意: 不带任何值的元组有个特殊的名称,叫做 单元(unit) 元组。这种值以及对应的类型都写作 ()
,表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。
另一个包含多个值的方式是 数组(array)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,Rust 中的数组长度是固定的。
fn main() {
let arr=[1,2,3,4,5,6,7,8,9,10 ,10,11,12,13,14,15,16,17];
}
数组的空间是在栈上开辟的,他的使用主要就是在确定好元素的个数,数据类型的时候用比较好。其他的时候,用Vector比较好。这个我们放在后面讲解,学习过c++的应该知道这个。下面我们来看一下数组的定义有几种方式以及如何去访问。
fn main() {
let arr=[1,2,3,4,5,6,7,8,9,10 ,10,11,12,13,14,15,16,17];//传统的定义
println!("{},{},{},{}",arr[0],arr[1],arr[2],arr[3]);
let arr1:[i32;5]=[1,2,3,4,5]; //声明5个i32数据类型的数组
println!("{},{},{},{}",arr1[0],arr1[1],arr1[2],arr1[3]);
let arr2=[3;5]; //创建5个数据的数组,且每个元素都是3; 3为数组起始值,5为个数.
println!("{},{},{}",arr1[0],arr1[1],arr1[2]);
}
本节的内容就到此结束了,这届主要讲的就是Rust中的变量以及一些数据类型,这一节的难度不是很大,只要认真看,很快就能学会。所以,这里就不过多介绍了,我们下节见!