rust快速入门
概述
- 强类型静态语言
- 编译型语言,运行性能比较高
- 运行时内存安全,拥有自己独特所有权管理机制,生命周期等来保证资源的释放等
- 后端语言
安装
- 前往官网安装rust编译工具
- mac上安装示例
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
### 安装完毕后查看状态
$ rustc -V
rustc 1.58.1 (db9d1b20b 2022-01-20)
- 配置vscode环境
- 安装rls插件
- 安装native debug插件
- 安装rust-analyzer插件
- 配置国内镜像源(编辑~/.cargo/config)
# 源码地址
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'tuna'
#replace-with = 'ustc'
#replace-with = 'sjtu'
#replace-with = 'rustcc'
#replace-with = 'aliyun'
# 镜像地址
# 清华大学
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
# 中国科学技术大学
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
# 上海交通大学
[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index"
# rustcc社区
[source.rustcc]
registry = "git://crates.rustcc.cn/crates.io-index"
# 阿里云
[source.aliyun]
registry = "https://code.aliyun.com/rustcc/crates.io-index"
- 第三方库查询地址
hello world
rust
- 使用cargo命令创建项目
cargo new test
- 需要先编译,再执行
fn main() {
println!("Hello, world!");
}
// 这里main是入口函数
- 编译
$ cargo build
Compiling test1 v0.1.0 (/Users/rockontrol/Desktop/js_code/test/rs-test/test1)
Finished dev [unoptimized + debuginfo] target(s) in 1.59s
- 执行
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/test1`
Hello, world!
rust的包管理
- 创建项目
cargo new app-xxx # 创建项目并提供包配置文件
- 查询第三方库
- 查询第三方库
- 配置.toml依赖
...
[dependencies]
rand = "0.8.0"
- 预编译/编译
cargo build # 预编译的目的是预先下载第三方库,为编写代码提供代码提示
- 使用第三方库案例
use rand::prelude::*;
fn main() {
let num: i32 = random();
println!("Hello, world! --> {}", num);
}
// 这里main是入口函数
- 运行
cargo run # 编写好代码后,即可直接运行
- 自定义包
- 创建目录lib
- 创建一个lib/math.rs
pub fn add(a: i8, b: i8) -> i8 {
return a + b;
}
- 创建一个lib/mod.rs
// 这个文件相当于python的__init__.py文件,必须有这个文件才能将这个目录视为包
pub mod math;
- 使用自定义包
mod lib; // 这里导入自定义的包
fn test_pkg() {
let a = lib::math::add(1, 2);
println!("{}", a)
}
函数定义
fn <函数名> ( <参数> ) <函数体>
变量定义
fn variable() {
let number1: i8 = 1; // 声明一个数字类型,但是变量不能被修改
println!("number1 --> {}", number1);
let mut number2: i8 = 2; // 声明一个数字类型,但是变量可以被修改
println!("number2 --> {}", number2);
number2 = 3;
println!("number2 --> {}", number2);
const Ok: bool = true; // 声明一个bool类型,但是变量不能被修改
println!("Ok --> {}", Ok);
let s: String = String::from("hello world"); // 声明一个字符串类型
println!("s --> {}", s);
let s1 = "你好".to_string();
println!("s1 --> {}", s1);
}
基本运算符
fn operator_symbel() {
// 主要涉及 + - / * 等
// +=,+,-,*,/
let mut a: i8 = 1;
a += 1;
println!("{} += ", a); // ++ -- 不支持
a = a * 10;
println!("{} * ", a);
a = a / 2;
println!("{} / ", a);
a = a - 1;
println!("{} - ", a);
//
}
布尔类型
fn test_bool() {
// 如果需要修改变量的值,则需要mut关键字
let mut b: bool = true;
println!("{} == true", b);
b = false;
println!("{} == true", b);
}
数字类型
fn test_number() {
// i8 u8
// i16 u16
// i32 u32
// i64 u64
// i128 u128
// isize usize
// f32 f64
let mut num: i32 = 2000000000;
println!("{} ", num);
num = num + 2;
println!("{}", num);
let num1 = num as i8;
println!("{}", num1);
}
字符串类型
fn test_string() {
let mut s = String::new(); // 空字符串
s = s + "hello world";
println!("{}", s);
let s1 = 1.to_string() + "2"; // 将数字转字符串,并拼接
println!("{}", s1);
let mut hello = String::from("hello");
println!("{}", hello);
hello.push_str(" world"); // 追加字符串
println!("{}", hello);
let s2 = format!("{}-{}-{}", s1, s, hello); // 使用format!宏来拼接
println!("{}", s2);
println!("{}", s2.len())
println!("{:?}", &s2[0..3]); // 切片
}
指针引用/所有权问题
- 在rust中变量一旦被借用,那么原变量将会失效
- 变量借用
fn test_point() {
// 在rust中通过生命周期与所有权来管理内存分配问题
fn point_(mut n: String) -> String {
println!("{}", n);
n
}
let mut s: String = String::from("hello world"); // 声明一个字符串类型
point_(s); // 此时该变量将被point_函数引用,一旦被引用,外面的s将不能再使用,也就是s变量外部已失效,但是基本类型i8,i64等除外
println!("{}", s) // 此时将失效,将提示值已被借用
}
- 引用借用
fn test_point_1() {
// 在rust中通过生命周期与所有权来管理内存分配问题
fn point_(mut n: &String) -> String {
println!("{}", n);
let s = format!("{} {}", n, String::from("!!"));
return s;
}
let mut s: String = String::from("hello world"); // 声明一个字符串类型
s = point_(&s); // 此时该变量的地址将被point_函数引用,此时地址被引用,外部变量没有失效,也就是变量s没有被销毁
println!("{}", s) // 此时s仍然可以被安全使用
}
复合类型类型
- 切片(Slice)是对数据值的部分引用
fn test_mul() {
// 元组类型,可以包含不同的数据类型
let tup1: (i8, i32, String) = (1, 2, "hello world".to_string());
println!("{}", tup1.0); // 元组根据索引取值,注意取值不可超出索引边界
println!("{}", tup1.1);
println!("{}", tup1.2);
// 数组类型,则要求列表中每个元素的类型必须一致
let arr = [1, 2, 3, 4];
println!("{}", arr[0]); // 数组根据索引取值
println!("{}", arr[3]); // 注意取值不可超出索引边界
println!("{:?}", &arr[0..2]); // 切片
println!("{:?}", &arr)
}
条件与循环
fn test_if_for() {
// 条件
let num: i8 = 3;
if num > 3 {
println!(" > 3")
} else if num == 3 {
println!(" == 3")
} else {
println!(" < 3")
}
// 不同的可迭代对象,方法可能不一样
// 循环遍历字符串
let s: String = String::from("hello world");
for i in s.chars() {
println!("{}", i)
}
// 循环遍历字符串
let arr = [1, 2, 3, 4];
for i in arr.iter() {
println!("{}", i)
}
// 根据索引来遍历取值
for i in 0..arr.len() {
println!("i {}", i);
println!("{}", arr[i])
}
// while 循环
let mut index: usize = arr.len() - 1;
while index >= 0 {
println!("index {}", index);
println!("{}", arr[index]);
if index == 0 {
break; // 这里感觉不需要,但是执行会报错,编译器问题
}
index = index - 1;
}
// loop循环
let mut index: usize = arr.len() - 1;
loop {
println!("index {}", index);
println!("{}", arr[index]);
if index == 0 {
break; // 这里感觉不需要,但是执行会报错,编译器问题
}
index = index - 1;
}
}
面向对象
// 定义结构体对象,与go语言类似
struct Huamn {
name: String,
age: i8,
}
impl Huamn {
// 定义对象方法,写法与python有点类似,self代表实例对象本身
// 类静态方法,关联方法
fn say(word: String) {
println!("人会说 {}", word)
}
// new方法也是一个关联函数,此方法相当于python的init,其他语言的new
fn new(name: String, age: i8) -> Huamn {
Huamn {
name: name,
age: age,
}
}
// 实例方法
fn eat(&self, food: &String) {
println!("{} 喜欢吃 {}", self.name, food)
}
}
fn test_object() {
let xh = Huamn {
name: String::from("xiaohong"),
age: 1,
};
xh.eat(&String::from("rice"));
Huamn::say(String::from("hi"));
}
枚举类型
// 枚举类型
enum Sport {
football,
basketball,
otherball,
play { k: String },
}
fn test_enum() {
let ball = Sport::play {
k: String::from("aaa"),
};
println!("{:?}", ball);
// match 匹配相当于其他的语言的swich,case
match ball {
Sport::football => {
println!("{:?}", ball)
}
Sport::basketball => {
println!("{:?}", ball)
}
Sport::otherball => {
println!("{:?}", ball)
}
Sport::play { k } => {
println! {"{}",k}
}
}
}
集合/map/向量类型
// 集合/向量/map类型
fn test_obj() {
// 向量也可以看成是数组
let mut arr = vec![1, 2, 3];
println!("{:?}", arr);
println!("{}", arr[1]);
println!("{:?}", arr.get(0));
// 使用match
match arr.get(0) {
Some(value) => println!("value {}", value.to_string()),
None => println!("none"),
}
// map是一个键值对形式的数据结构,与python,go等类似
let mut dict = std::collections::HashMap::new();
dict.insert("name", "小明"); // 插入新数据,或更新
dict.insert("age", "12");
println!("{:?}", dict);
println!("{:?}", dict.get("name"));
dict.entry("name").or_insert("小红"); // 不存在则插入
println!("{:?}", dict.get("name"));
for i in dict.iter() {
// iter 是一个可迭代的对象
println!("dict {:?} {}", i.0, i.1);
}
// 集合是一个和map相似的数据结构,但是他是只有键没有值的,或者理解为值为空的map
let mut set = std::collections::HashSet::new();
set.insert("football");
println!("{:?}", set);
for i in set.iter() {
//iter 是一个可迭代的对象
println!("set {:?}", i);
}
}
错误处理
// 错误处理
// 在rust中错误处理有两种,对于可恢复错误用 Result<T, E> 类来处理,对于不可恢复错误使用 panic! 宏来处理
// enum Result<T, E> {
// Ok(T),
// Err(E),
// }
fn test_exception() {
let f = std::fs::File::open("hello.txt");
match f {
Ok(file) => {
println!("文件打开正常")
}
Err(err) => {
println!("文件打开异场 {}", err) // 这个一场被正常的处理了,程序没有panic
}
}
panic!("文件打开异常,建议bug下") // 这个主要抛出异常,使得程序崩溃终止了
}
并发编程
- 安全高效的处理并发是 Rust 诞生的目的之一,主要解决的是服务器高负载承受能力
- Rust 不依靠运行时环境,这一点像 C/C++ 一样
- Rust 在语言本身就设计了包括所有权机制在内的手段来尽可能地把最常见的错误消灭在编译阶段,这一点其他语言不具备
- Rust 中标准库中包含了多进程与多线程,且进程之间的通信方式使用channel,lock来实现
fn deal_fn(i: i8) {
// 处理函数
println!("child thread {}", i)
}
async fn deal_async(i: i8) {
// 协程处理函数
println!("child async {}", i)
}
pub fn test_thread() {
let mut theads: Vec<std::thread::JoinHandle<()>> = Vec::new();
for i in 0..5 {
let mut handle = std::thread::spawn(|| deal_fn(1));
theads.push(handle);
}
println!("等待子线程结束");
for thread_ in theads {
thread_.join().unwrap()
}
println!("完成")
}
pub fn test_thread_pool() {
// threadpool = "1.0" 依赖项
let pool = threadpool::ThreadPool::new(4);
for i in 0..100 {
pool.execute(move || deal_fn(i))
}
println!("等待子线程结束");
pool.join();
println!("完成");
}
pub fn test_async() {
// 协程
let mut q = vec![];
for i in 0..100 {
q.push(deal_async(i));
}
for i in q {
futures::executor::block_on(i); // 这里是阻塞式,相当于wait
}
println!("完成")
}
继承与多态
- 多态则可以根据trait特性来实现
- trait其实看起来更像是一种抽象类,也就是下面的的继承更像是对抽象类的重新
// 继承多态
// 在rust中,如果要真正实现继承还没有更好的办法,如果非要做,只好定义新struct来嵌套实现,这一点与go类似
pub trait Man {
fn own(&self);
}
pub struct Human {
name: String,
age: i8,
}
impl Man for Human {
fn own(&self) {
println!("所有人都会说话")
}
}
pub fn test_trait() {
let hm = Human {
name: "man".to_string(),
age: 123,
};
hm.own();
}
特性
- 在rust中可以通过范型机制来表达抽象的方法,以达到通用
fn first<T: PartialOrd + Copy + std::cmp::Ord>(arr: &[T]) -> T {
// 实现取数组的第一个值
// arr.sort();
arr[0]
}
pub fn test_T() {
let mut arr = [1, 2, 3, 4];
&arr.sort();
let mut arr1 = ["b", "a", "c"];
&arr1.sort();
println!("{}", first(&arr));
println!("{}", first(&arr1));
}