https://blog.csdn.net/bbdxf/article/details/78798809
很多基础的类型和操作都很简单,不做说明。下面仅列举一些最容易产生阻碍性疑问的点。
&
引用*
解引用一个及其重要,但是与实际“看似”矛盾的问题,借用(borrow):
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let a:i32 = 10;
let b = a;
println!("{:?} {:?}", a, b);
let p1 = Point { x: 10, y: 20 };
let p2 = p1;
println!("{:?} {:?}", p1, p2);
}
/*
编译错误:
error[E0382]: borrow of moved value: `p1`
13 | let p2 = p1;
| -- value moved here
14 | println!("{:?} {:?}", p1, p2);
| ^^ value borrowed here after move
*/
上面的代码,看似a/b
与p1/p2
行为一模一样,为什么a/b
可正常访问,但是p1/p2
就不行?
在Rust中,有一个叫做借用(borrow)的东西,它会将变量的使用权转移,转以后,原始变量就会变得不可用。这对于很“纯洁”的数据类型/结构是适用的,比如我们创建的Point
类型。
原则上而言,let p1 = Point{x:10, y:20};
创建了一个p1对象,此时正常访问没问题,然后let p2 = p1;
,它将p1
的所有权转交(borrow of move value)给p1
,此时p1
就无法在此被正常访问了,只有p2
可以。这个流程是符合Rust关于borrow的设计原则的。
那,问题来了?为什么a/b
就可以,它们违背了Rust的设计原则了吗?
答案肯定是:没有违背!原因在于,Rust不仅有借用原则,还有Copy/Clone
默认行为!这一点在大部分教程中都没有作为关键点提出,所以,不明白的童鞋看到上述代码就会很迷茫。由于Rust有部分类型默认实现了std::marker::Copy
trait, 也就是整型浮点型这类基本类型。像 structs 这类没有默认实现的类型, 想要这样就得实现一下Copy/Clone
.
#[derive(Debug)]
struct Point {
x: f64,
y: f64,
}
impl Clone for Point {
fn clone(&self) -> Self {
Self { x: self.x, y: self.y }
}
}
impl Copy for Point {}
现在终于好使了。但是我们发觉做这些操作非常烦, 我们注意到 #[derive(Debug)]
这个东西, 刚好 Rust 提供了 Clone , Copy
的属性, 所以最终代码为:
#[derive(Debug, Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let a = 10;
let b = a;
println!("{:?} {:?}", a, b);
let p1 = Point { x: 10, y: 20 };
let p2 = p1;
println!("{:?} {:?}", p1, p2);
}
/*
10 10
Point { x: 10, y: 20 } Point { x: 10, y: 20 }
完美!
*/
基本类型解决了,我们处理下传参问题,以及指针问题:
一个函数传参的Demo:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn func1(a: i32) -> i32 {
println!("fun1 {:?}", a);
a * 2
}
fn func2(a: &mut i32) {
println!("fun2 {:?}", a);
*a = 111;
}
fn func3(p: Point) -> Point {
println!("fun3 {:?} {:?}", p.x, p.y);
Point { x: 1, y: 2 }
}
fn func4(p: &mut Point) {
println!("fun4 {:?} {:?}", p.x, p.y);
p.x = 23;
p.y *= 2;
}
// func5
fn func5() -> &String{
let s = String::from("21313");
&s // error! s is out of it's lifetime
}
fn main() {
let a = 10;
println!("{:?} {:?}", func1(a), a);
let mut b = a;
func2(&mut b);
println!("{:?} {:?}", a, b); // a has copy trait
let p1 = Point { x: 100, y: 200 };
let p = func3(p1);
// println!("{:?} {:?}", p1.x, p1.y); // p1 has not copy trait, so error
println!("{:?} {:?}", p.x, p.y);
// func4(&mut p); error for p is not mut
let mut p2 = p;
func4(&mut p2);
println!("{:?} {:?}", p2.x, p2.y);
}
指针问题,单独一个Demo:
fn main() {
let x = 5;
let raw = &x as *const i32;
// println!("raw points at {}", *raw); *raw is unsafe
println!("{:?}", raw); // raw is address of x, access is ok
let pt_raw = unsafe { *raw }; // 明确指明unsafe
println!("{:?}", pt_raw); // ok
let raw2 = &x;
println!("{:?}", raw2); // access raw2 is value of x, raw2 is a ref of x (safe ref)
let pt_raw2 = raw2 as *const i32;
println!("{:?}", pt_raw2); // ok, pt_raw2 == raw
let a:i32 = 123;
let b = a;
let p1 = &a as *const i32;
let p2 = &b as *const i32;
println!("{:?} {:?}", p1, p2);
}
/*
0x7ce7d6f814
5
5
0x7ce7d6f814
0x7ce7d6f978 0x7ce7d6f97c
*/
一个重要的数据类型string:
fn main() {
let s1: &str = "hello world, china!"; // 常量字符串,生命周期整个程序
let s2: String = String::from(s1);
let mut s3: String = s1.to_string();
let s4: &str = s2.as_str(); // == &s2
s3.push_str(" 1233 ");
let s5 = s3.clone() + "#@!"; // 直接使用s3会被borrow,无法再次访问
println!("{:?} {:?} {:?}", s4, s3, s5);
let s6 = "001.忠犬ハチ公";
println!("len1: {:?}", s6.len()); // UTF-8字节长度
println!("len2: {:?}", s6.chars().count()); // Unicode长度
// 按照UTF-8编码,逐字节遍历
for (i, bt) in s6.as_bytes().iter().enumerate(){
print!("{:?}=>{:?} ", i, bt)
}
println!();
// 按照Unicode编码,逐字符遍历
for (i, ch) in s6.chars().enumerate(){
print!("{:?}=>{:?} ", i, ch)
}
}
/*
len1: 19
len2: 9
0=>48 1=>48 2=>49 3=>46 4=>229 5=>191 6=>160 7=>231 8=>138 9=>172 10=>227 11=>131 12=>143 13=>227 14=>131 15=>129 16=>229 17=>133 18=>172
0=>'0' 1=>'0' 2=>'1' 3=>'.' 4=>'忠' 5=>'犬' 6=>'ハ' 7=>'チ' 8=>'公'
*/
Slice, 特殊的是需要使用&
才可使用:
fn main() {
let mut a = [1, 2, 3, 4];
a[2] = 10;
println!("{:?}", a[2]);
let b = &a[..];
println!("{:?}", b);
let c = &a[1..3]; // 不包含最后一个
println!("{:?}", c);
let s1 = "hello,world";
println!("{:?}", &s1[..=5]); // ..= 包含最后一个字符
}
/*
10
[1, 2, 10, 4]
[2, 10]
"hello,"
*/
动态数组:
fn main() {
let mut a= vec![25,21,3];
println!("{:?}", a);
for i in &mut a{
*i = *i * 10;
}
println!("{:?}", a);
}
/*
[25, 21, 3]
[250, 210, 30]
*/
元组:
fn func() -> (i32, f32){
(231, 2.311)
}
fn main() {
let t1 = (23, "1312");
println!("{:?}", t1);
let t = func();
let (a,_) = func();
println!("{:?} {} ", t, a);
}
/*
(23, "1312")
(231, 2.311) 231
*/
Rust中的值都可以是一个表达式:
fn func(a: i32) -> i32 {
if a < 10 {
a * 10
} else {
a / 10
}
}
fn main() {
println!("{}", func(9));
let a = {
let y = 10;
y + 1
};
let b = if a > 10 {
true
} else {
false
};
println!("{} {}", a, b);
}
循环,比较特别的是loop可以有返回值:
fn main() {
// if
// ...
// loop
let mut i = 0;
loop{
println!("loop to dead! {}", i);
i += 1;
if i>5{
break;
}
}
// loop can return value
i = 1;
let a = loop{
i *= 2;
if i>1000 {
break i;
}
};
println!("{}", a);
// for
let b = [23, 11, 123, 11];
for (i,v) in b.iter().enumerate(){
println!("{} {}",i, v);
}
}
即,在同一作用域中,要么同时存在多个不可变引用,要么仅存在一个可变引用,其他的情况都违规。
fn main(){
let mut a = String::from("23123");
let b = &mut a; // ok
let c = &mut a; // error, 同一作用域中,不能同时有两个可变引用
}
fn main() {
let mut a = String::from("asdasd");
let b = &a; // ok
let c = &mut a; // error, 同一作用域中,不能同时存在可变/不可变引用
println!("{} {}", c, b);
}
Lifetime annotations have a slightly unusual syntax: the names of lifetime parameters must start with an apostrophe ('
) and are usually all lowercase and very short, like generic types. Most people use the name 'a
. We place lifetime parameter annotations after the &
of a reference, using a space to separate the annotation from the reference’s type.
&i32 // a reference
&'a i32 // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
One special lifetime we need to discuss is 'static, which denotes the entire duration of the program. All string literals have the 'static lifetime, which we can annotate as follows:
let s: &'static str = "I have a static lifetime.";
一个关系到生命周期的隐形例子
struct Point {
x: i32,
y: i32,
}
struct PointRef<'a> {
x: &'a mut i32,
y: &'a mut i32,
}
fn main() {
let mut p1 = Point { x: 10, y: 12 };
p1.x = 100;
{
println!("Point: {}, {}", p1.x, p1.y); // 1, 这里可以用. immutable borrow
let p2 = PointRef { x: &mut p1.x, y: &mut p1.y }; // mutable borrow
*p2.y = 111;
// println!("Point: {}, {}", p1.x, p1.y); // 2, 这里不行. immutable borrow
println!("PointRef: {} {}", p2.x, p2.y);
}
}
结构体和接口
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 { // 方法
self.width * self.height
}
}
impl Rectangle { // 这里,允许使用多个impl块来定义 Rectangle
fn square(size: u32) -> Rectangle { // 关联函数,没有self参数,类似静态方法调用
Rectangle { width: size, height: size }
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("The area of the rectangle is {} square pixels.",rect1.area());
}
枚举:
enum EnumA {
IPV4,
IPV6,
None
}
enum EnumB {
Man(String),
Woman(String),
None
}
fn main() {
let a = EnumA::IPV4;
let b = EnumB::Man(String::from("123"));
let c = EnumB::Woman(String::from("abc"));
// if b==c ..., error, can't compare with ==
println!("{:?}", match a {
EnumA::IPV4 => 1,
EnumA::IPV6 => 2,
_ => 0
});
println!("{:?}", match b {
EnumB::Man(v) => v,
EnumB::Woman(v) => v,
_ => "None".to_string(),
});
// 简单比较
if let EnumA::IPV4 = a {
println!("a is IPV4");
}
if let EnumB::Woman( v ) = c {
println!("c is woman {:?}", v);
}
}
针对枚举书写代码过多,使用Option进行优化:
fn plus_one(x: Option) -> Option {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
MAP类型:
fn main() {
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
let score = scores.get(&team_name);
println!("{} {}", team_name, score.unwrap());
for (key, value) in &scores {
println!("{}: {}", key, value);
}
let count = scores.entry("Balck".to_string()).or_insert(0); // 存在了获取,不存在就设置为0
*count += 1;
}
错误处理
use std::fs::File;
use std::io::ErrorKind;
fn main() {
// 1
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("There was a problem opening the file: {:?}", error)
},
};
// 2
let f = File::open("hello.txt").map_err(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Tried to create file but there was a problem: {:?}", error);
})
} else {
panic!("There was a problem opening the file: {:?}", error);
}
});
// 3
let f = File::open("hello.txt").unwrap();
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}
// 使用 ? 传播错误, Ok()包裹正确的值
fn read_username_from_file() -> Result {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
闭包/匿名函数
let expensive_closure = |num: u32| -> u32 {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
};
// normal
let mut num = 5;
{
let mut add_num = |x: i32| num += x;
add_num(5);
}
assert_eq!(10, num);
// move
let mut num = 5;
{
let mut add_num = move |x: i32| num += x;
add_num(5);
}
assert_eq!(5, num);
智能指针:
Box
在堆上分配Rc
有引用计数的包裹类型,可以被多个所有者使用Ref
和RefMut
,通过 RefCell
访问,一个在运行时而不是在编译时执行借用规则的类型Arc
原子引用计数,并发安全List的实现:
enum List {
Cons(i32, List),
Nil,
}
use List::{Cons, Nil};
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}
use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;
use std::thread;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
thread::spawn(|| {
handle_connection(stream);
});
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
let response = "HTTP/1.1 200 OK\r\n\r\n";
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
use List::*;
enum List {
// Cons: 元组结构体,包含一个元素和一个指向下一节点的指针
Cons(u32, Box),
// Nil: 末结点,表明链表结束
Nil,
}
// 方法可以在 enum 定义
impl List {
// 创建一个空列表
fn new() -> List {
// `Nil` 为 `List` 类型
Nil
}
// 处理一个列表,得到一个头部带上一个新元素的同样类型的列表并返回此值
fn prepend(self, elem: u32) -> List {
// `Cons` 同样为 List 类型
Cons(elem, Box::new(self))
}
// 返回列表的长度
fn len(&self) -> u32 {
// `self` 必须匹配,因为这个方法的行为取决于 `self` 的变化类型
// `self` 为 `&List` 类型,`*self` 为 `List` 类型,一个具体的 `T` 类型的匹配
// 要参考引用 `&T` 的匹配
match *self {
// 不能得到 tail 的所有权,因为 `self` 是借用的;
// 而是得到一个 tail 引用
Cons(_, ref tail) => 1 + tail.len(),
// 基本情形:空列表的长度为 0
Nil => 0
}
}
// 将列表以字符串(堆分配的)的形式返回
fn stringify(&self) -> String {
match *self {
Cons(head, ref tail) => {
// `format!` 和 `print!` 类似,但返回的是一个堆分配的字符串,而不是
// 打印结果到控制台上
format!("{}, {}", head, tail.stringify())
},
Nil => {
format!("Nil")
},
}
}
}
fn main() {
// 创建一个空链表
let mut list = List::new();
// 追加一些元素
list = list.prepend(1);
list = list.prepend(2);
list = list.prepend(3);
// 显示链表的最后状态
println!("linked list has length: {}", list.len());
println!("{}", list.stringify());
}
闭包只有这三种模式,即
https://rustwiki.org/zh-CN//rust-by-example
fn is_odd(n: u32) -> bool {
n % 2 == 1
}
fn main() {
println!("Find the sum of all the squared odd numbers under 1000");
let upper = 1000;
// 命令式方式(imperative approach)
// 声明累加器变量
let mut acc = 0;
// 重复:0,1, 2, ... 到无穷大
for n in 0.. {
// 数字的平方
let n_squared = n * n;
if n_squared >= upper {
// 若大于上限(upper limit)则退出循环
break;
} else if is_odd(n_squared) {
// 如果是奇数就累加值
acc += n_squared;
}
}
println!("imperative style: {}", acc);
// 函数式方式(functional approach)
let sum_of_squared_odd_numbers: u32 =
(0..).map(|n| n * n) // 所有自然数的平方
.take_while(|&n| n < upper) // 小于上限
.filter(|&n| is_odd(n)) // 为奇数
.fold(0, |sum, i| sum + i); // 最后其后
println!("functional style: {}", sum_of_squared_odd_numbers);
}
模块默认是私有的,并且只要中间有一个私有的,其后路径上pub的也不能访问。
// 一个名为 `my` 的模块
mod my {
// 在模块中的项默认带有私有可见性。
fn private_function() {
println!("called `my::private_function()`");
}
// 使用 `pub` 修饰语来改变默认可见性。
pub fn function() {
println!("called `my::function()`");
}
// 在同一模块中,项可以访问其它项,即使是私有属性。
pub fn indirect_access() {
print!("called `my::indirect_access()`, that\n> ");
private_function();
}
// 项也可以嵌套。
pub mod nested {
pub fn function() {
println!("called `my::nested::function()`");
}
#[allow(dead_code)]
fn private_function() {
println!("called `my::nested::private_function()`");
}
}
// 嵌套项的可见性遵循相同的规则。
mod private_nested {
#[allow(dead_code)]
pub fn function() {
println!("called `my::private_nested::function()`");
}
}
}
fn function() {
println!("called `function()`");
}
fn main() {
// 模块允许在拥有相同名字的项之间消除歧义。
function();
my::function();
// 公有项,包括内部嵌套的公有项,可以在父级的模块中访问到。
my::indirect_access();
my::nested::function();
// 一个模块中的私有项不能被直接访问,即使私有项嵌套在公有的模块中:
// 报错!`private_function` 是私有的。
//my::private_function();
// 试一试 ^ 将此行注释去掉
// 报错! `private_function` 是私有的。
//my::nested::private_function();
// 试一试 ^ 将此行注释去掉
// 报错! `private_nested` 是私有的模块。
//my::private_nested::function();
// 试一试 ^ 将此行注释去掉
}
结构体成员也默认是私有的。
mod my {
// 一个公有的结构体,带有一个公有的泛型类型 `T` 的字段
pub struct WhiteBox {
pub contents: T,
}
// 一个公开的结构体,带有一个私有的泛型类型 `T` 的字段
#[allow(dead_code)]
pub struct BlackBox {
contents: T,
}
impl BlackBox {
// 一个公有的构造器方法
pub fn new(contents: T) -> BlackBox {
BlackBox {
contents: contents,
}
}
}
}
fn main() {
// 带有公有字段的公有的结构体,可以像平常一样构造
let white_box = my::WhiteBox { contents: "public information" };
// 并且它们的字段可以正常访问到。
println!("The white box contains: {}", white_box.contents);
// 带有私有字段的公有结构体不能使用字段名来构造。
// 报错!`BlackBox` 含有私有字段。
//let black_box = my::BlackBox { contents: "classified information" };
// 试一试 ^ 将此行注释去掉
// 不过带有私有字段的结构体可以使用公有的构造器来创建。
let _black_box = my::BlackBox::new("classified information");
// 并且一个结构体中的私有字段不能访问到。
// 报错!`content` 字段是私有的。
//println!("The black box contains: {}", _black_box.contents);
// 试一试 ^ 将此行注释去掉
}
按照文件划分模块:
|-- my
| |-- inaccessible.rs // mod inaccessible;
| |-- mod.rs // 模块总入口,管理这个文件夹下pub内容
| `-- nested.rs // mod nested;
`-- main.rs // 主调用
rustc 默认编译的是库,想要可执行文件需要添加参数改变它的行为。rustc --crate-type=lib rary.rs
编译为libxxx.rlib。其他程序可以从外部导入库:extern crate rary;
然后编译时添加参数rustc executable.rs --extern rary=libxxxx.rlib
就可以啦。
如果是在项目中使用其他人编写的库,也是同样extern crate rary;
。此时,也需要在Cargo.toml
中添加dependencies
中库对应的版本信息。
属性可以接受参数,有不同的语法形式:
#[attribute = "value"]
#[attribute(key = "value")]
#[attribute(value1, value2, ...)]
一些固定的属性:
// 这个 crate 是一个库文件
#![crate_type = "lib"]
// 库的名称为 “rary”
#![crate_name = "rary"]
//////////////////////////////////
// 这个函数仅当操作系统是 Linux 的时候才会编译
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
println!("You are running linux!")
}
// 而这个函数仅当操作系统**不是** Linux 时才会编译
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
println!("You are *not* running linux!")
}
fn main() {
are_you_on_linux();
println!("Are you sure?");
if cfg!(target_os = "linux") {
println!("Yes. It's definitely linux!");
} else {
println!("Yes. It's definitely *not* linux!");
}
}
自定义属性/配置:
#[cfg(some_condition)]
fn conditional_function() {
println!("condition met!")
}
// rustc --cfg some_condition custom.rs && ./custom
// 否则,会提示编译找不到 conditional_function
trait是定义特性,impl是实现定义的特性。
如下两种方式实现:
trait Pilot {
fn fly1(&self);
}
trait Wizard {
fn fly2(&self);
}
struct Human;
impl Pilot for Human {
fn fly1(&self) {
println!("This is your captain speaking.");
}
}
impl Wizard for Human {
fn fly2(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}
fn main() {
let a = Human {};
a.fly();
a.fly1();
a.fly2();
}