Rust 一门赋予每个人构建可靠且高效软件能力的语言。具有高性能、可靠性、和生产力三大特点。
接下来就从安装,开发环境搭建、基础概念和语法了解三个部分介绍第一周学习Rust的情况。默认为Mac环境
0、安装Rust
优先建议使用rustup工具进行安装操作
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
另外如果本地已经安装了较低的版本,则可以通过rustup update
进行版本升级操作,经过一阵子的等待之后,rust安装文案提示成功即可,还需要配置环境变量
可以在用户当前的根目录下的.bash_profile
文件末尾添加如下配置
# rust
export PATH=$PATH:$HOME/.cargo/bin
最后进行source .bash_profile
刷新即可。通过如下操作检验是否安装成功,参数一定得是大写的V
➜ ~ cargo -V
cargo 1.51.0 (43b129a20 2021-03-16)
➜ ~ rustc -V
rustc 1.51.0 (2fd73fabe 2021-03-23)
cargo Rust构建工具和包管理器,在安装Rust的时候,会自动安装Cargo
-
cargo build
构建项目 -
cargo run
运行项目 -
cargo test
测试项目 -
cargo doc
构建项目文档 -
cargo publish
将当前的库发布到cartes.io 上
至此整个的运行环境就已经搭建完毕
1、开发环境搭建
VS Code
作为开发环境,安装好Rust的相关插件,插件rls
,,
- Rust代码支持,rls
- Rust包版本管理工具,crates
-
TOML文件高亮支持
在终端环境输入cargo new rust-learn
就会自动的创建rust-learn项目,其中的src文件包含了入口文件main.rs
运行也就可以直接在终端输入cargo run
即可,出现的warning只是警告信息,并不会影响程序的执行,而Error则会停止服务的运行
至此,已经可以开始进行demo的编写,语法的学习以及正常的代码开发工作了。
2、基础知识学习
- 官方Rust学习文档
- Rust学习资料在线网站
- Rust教程
Rust的突击学习一周,感觉Rust是个大杂烩,包含了多种语言的相关特点,例如:
- 变量声明的时候采用了JS的规则,可是其又是一个
强类型的语言
,Rust可以手动自定义类型也可以根据具体数据推测其数据类型; - 默认是不可变的,如果需要变成可变的则需要手动添加
mut可变
关键字,这个和scala类似; - 内存管理则吸收了C和Java的特点,C里面需要手动申请和释放内存,而Java则是由JVM统一管理,Rust则是自动申请内存,而在当前的作用域完成之后,自动的添加相关内存释放的操作;
- 有类似scala的trait特性,其初步理解和接口比较接近,默认返回一个数据的时候可以不用写
return关键字
- 有类似C语言的
&
指针的概念,为数据的所有权做好足够的约束
说了这么多,接下来就来学习一个Rust吧~
数据类型
Rust是一门强类型的语言,其类型分为整型
,浮点型
,布尔类型
,字符类型
几种类型,在使用的时候都应该带上具体是8位还是16位的相关说明,而不是和java一样,完全由JVM屏蔽了其对象大小的细节情况,从另一个角度来看,也可以节省内存吧。
- 整型
位长度 | 有符号 | 无符号 | 表达形式 |
---|---|---|---|
8-bit | i8 | u8 | 0o77,0o16 |
16-bit | i16 | u16 | 0xff,0x12 |
32-bit | i32 | u32 | - |
64-bit | i64 | u64 | - |
128-bit | i128 | u128 | - |
arch | isize | usize | - |
- 浮点型
就f32
和f64
两种类型
- bool布尔类型
就true
和false
两种
- 字符型
char
字符类型,一般是4个字节,因为是4个字节,那么就可以支持中文、日文、emoji表情等数据,使用UTF-8作为编码格式,注意使用GBK之后导致的乱码情况,使用单引号括起来
不可变,可变,重影,常量
代码Demo如下
fn test01() {
let a = "Hello";
// 1、a = "zhangsan"; 不可变对象
// 会提示错误 「cannot assign twice to immutable variable」
let a = "lisi";
// 2、重影
println!("a:{}", a);
let mut b = "Hello";
b = "zhangsan";
// 3、可变对象
println!("b:{}", b);
const Count:i32 = 32;
// 4、常量
println!("count:{}", Count);
}
运行结果:
Finished dev [unoptimized + debuginfo] target(s) in 1.22s
Running `target/debug/rust-learn`
a:lisi
b:zhangsan
使用let
关键字进行声明变量,而且默认是不可变对象,如果再对其进行赋值操作,则会出现错误「cannot assign twice to immutable variable」,这点和对Java的认识是不一样的,一旦固定下来了,就是不可变的。可以使用let mut
关键字设置可变对象。重影Shadowing则是给当前赋值对象重新指向一个数据
总结&理解:
默认不可变可能是Rust从高并发安全的角度设计的,尽量减少对数据的改值操作
重影shadowing则是一个新的概念,此外并没有接触,还是应该从内存管理的角度理解吧,如代码中2的操作之后,之前的Hello对象,就会被系统自动的remove
掉
常量必须指定数据类型
,而且应该是具体的数据表达式,而不是什么方法之类的,此外也不可用于重影操作
Rust对内存、安全的要求有点严苛
String、&str和所有权
先看看如下代码
fn test02() {
fn cp(content: String) {
println!("content:{}", content);
}
let a = "Hello World"; // 1
cp(a);
println!("a:{}", a);
}
代码很简单,大致也能知道只需要传入一个String的参数数据进行打印操作,然后把数据本身再打印一次,如果正常的话,则会输出两遍结果。但是意外的是,这个代码出现了2个错误
- 错误1 cp方法出现错误 expected struct
String
, found&str
报错意思很明显,希望传入的String类型,但是实际传入的则是&str,这就引出来了一个情况,1处的代码生命的对象实际上是&str
类型,而不是期望的String
。
- String是一个可变的、堆上分配的UTF-8的字节缓冲区,存在len()和capacity()方法
- str是一个不可变的固定长度的字符串,只有一个len()方法
-
&
不能仅仅理解成指针,实际上是租借borrowed的意思
那么怎么解决问题呢,把方法定义和传入的参数定义统一即可,都改成String,或者&str,甚至&String等
&str ==> String的几种方案
let a = "hello";
let b1 = a.to_string();
let b2 = String::from(a);
let b3 = a.to_owned();
String ==> &str的几种方案
let a = String::from("hello");
let b1 = &a;
let b2 = a.as_str();
- 错误2:最后的一行出现错误 value borrowed here after move
意思很清楚,数值被使用的时候已经被remove掉了,就无法使用,为什么呢?
主要原因是因为传入到cp方法的参数是对象a本身,a的作用域已经被移动到方法内部了,随着作用域的改变,在方法外的打印操作就属于作用域外的调用执行,所以就被理解成remove
掉了,那么自然就会出现失败的情况。再看一个下面的代码,加强对所有权的理解
fn main() {
let s = String::from("hello");
// s 被声明有效
takes_ownership(s);
// s 的值被当作参数传入函数
// 所以可以当作 s 已经被移动,从这里开始已经无效
let x = 5;
// x 被声明有效
makes_copy(x);
// x 的值被当作参数传入函数
// 但 x 是基本类型,依然有效
// 在这里依然可以使用 x 却不能使用 s
} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
fn takes_ownership(some_string: String) {
// 一个 String 参数 some_string 传入,有效
println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放
fn makes_copy(some_integer: i32) {
// 一个 i32 参数 some_integer 传入,有效
println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
fn main() {
let s1 = gives_ownership();
// gives_ownership 移动它的返回值到 s1
let s2 = String::from("hello");
// s2 被声明有效
let s3 = takes_and_gives_back(s2);
// s2 被当作参数移动, s3 获得返回值所有权
} // s3 无效被释放, s2 被移动, s1 无效被释放.
fn gives_ownership() -> String {
let some_string = String::from("hello");
// some_string 被声明有效
return some_string;
// some_string 被当作返回值移动出函数
}
fn takes_and_gives_back(a_string: String) -> String {
// a_string 被声明有效
a_string // a_string 被当作返回值移出函数
}
最后自己尝试着用各自方法解决一下demo中出现的错误
总结&理解:
String和str是完全不同的两种数据结构,默认定义一个字符串是str的类型
在使用方法的时候,写的参数一定需要注意到,是否有使用引用,是否被提前移除等情况
暂时就先总结这么多,后面的继续学习和更新