rust语言小记录
变量
变量分为不可变变量和可变变量,声明一个变量使用 let
关键词
let var1 = 10; // 不可变变量
var1 = 5; // error, var1是immutable的variable
let mut var2 = 4;
var2 = 5;
同时强类型,以下情形不被允许
let mut var = 123;
var = "abc"; // 当声明 a 是 123 以后,a 就被确定为整型数字,不能把字符串类型的值赋给它。
var = 10.5; // 自动转换数字精度有损失,Rust 语言不允许精度有损失的自动数据类型转换。
shadowing
重影就是指变量的名称可以被重新使用的机制:
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("The value of x is: {}", x);
}
重影与可变变量的赋值不是一个概念,重影是指用同一个名字重新代表另一个变量实体,其类型、可变属性和值都可以变化。但可变变量赋值仅能发生值的变化。
let a = "123";
let a = 10;// ok
let b = "456";
b = b.len();//error
另外,if this is intentional, prefix it with an underscore:_x
这表明存在一个未使用的变量 x,你可以将其删除或在变量名前添加一个下划线前缀;
数据类型
值得注意:rust不支持自增自减操作符
rust字符型char大小为4个字节,一般推荐使用字符串储存 UTF-8 文字(非英文字符尽可能地出现在字符串中)。
复合类型 tuple & list,甚至可以类型注解
tuple可以包含不同类型数据
let tup:(i32, f64, u8) = (100,6.5,1);
let (x, y, z) = tup;// y = 6.5
list只能包含同类型的数据
然后就是List不用标注mut, variable unneed to use mut
let a = [1, 2, 3, 4, 5];
// a 是一个长度为 5 的整型数组
let b = ["January", "February", "March"];
// b 是一个长度为 3 的字符串数组
let c: [i32; 5] = [1, 2, 3, 4, 5];
// c 是一个长度为 5 的 i32 数组
let d = [3; 5];
// 等同于 let d = [3, 3, 3, 3, 3];
let first = a[0];
let second = a[1];
// 数组访问
a[0] = 123; // 错误:数组 a 不可变
let mut a = [1, 2, 3];
a[0] = 4; // 正确
注释
和c一样的符号,可以写py中md一样的doc
函数
fn <函数名> ( <参数> ) <函数体>
Rust不在乎您在何处定义函数,只需在某个地方定义它们即可。
fn main() {
pp(5);
return;
}
fn pp(x: i32){
print!("{x}\n");
return;
}
Rust 函数体由一系列可以以表达式(Expression)结尾的语句(Statement)组成
// statement 执行某些操作且没有返回值的步骤
let a = 5;
// err, 没有返回值
// let a = (let b = 2);
// expression 有计算步骤且有返回值。以下是表达式(假设出现的标识符已经被定义):
a = 7
b + 2
c * (a + b)
Rust 中可以在一个用 {} 包括的块里编写一个较为复杂的表达式:
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("x 的值为 : {}", x);
println!("y 的值为 : {}", y);
}
很显然,这段程序中包含了一个表达式块:
而且在块中可以使用函数语句,最后一个步骤是表达式,此表达式的结果值是整个表达式块所代表的值。这种表达式块叫做函数体表达式。
注意:x + 1 之后没有分号,否则它将变成一条语句!
x的作用域,这里输出 x = 5, y = 4
返回值的方法类似py,但必须在参数声明后用 ->
fn pp() -> i64 {
return 5;
}
条件语句
类似c,条件可以不用 ()
,但是即使是单语句,block也必须用 {}
包括,
else if
条件表达式必须是bool类型
block可以是函数体表达式, 注意 :两个函数体表达式的类型必须一样!且必须有一个 else 及其后的表达式块
fn main() {
let a = 3;
let number = if a > 0 { 1 } else { -1 };
println!("number 为 {}", number);
}
循环
while
fn main() {
let mut number = 1;
while number != 4 {
println!("{}", number);
number += 1;
}
println!("EXIT");
}
for
fn main() {
let a = [10, 20, 30, 40, 50];
for i in a.iter() {
println!("值为 : {}", i);
}
}
// 下标
fn main() {
let a = [10, 20, 30, 40, 50];
for i in 0..5 {
println!("a[{}] = {}", i, a[i]);
}
}
loop循环
while(true)
,无限循环,中途break
经常被用来做查找工具,这里用遍历示范
fn main() {
let s = ['R', 'U', 'N', 'O', 'O', 'B'];
let mut i = 0;
let location = loop {
let ch = s[i];
if ch == 'O' {
break i;
}
i += 1;
};
println!(" \'O\' 的索引为 {}", location); // 'O' 的索引为 3
}
所有权
这里选几个独特的features
引用与租借
引用不会获得值的所有权。
引用只能租借(Borrow)值的所有权。
引用本身也是一个类型并具有一个值,这个值记录的是别的值所在的位置,但引用不具有所指值的所有权:
fn main() {
let s1 = String::from("hello");
let mut s2 = &s1;
let s3 = s1;
s2 = &s3; // 重新从 s3 租借所有权
println!("{}", s2);
}
s2 租借的 s1 已经将所有权移动到 s3,所以 s2 将无法继续租借使用 s1 的所有权。如果需要使用 s2 使用该值,必须重新租借
引用不具有所有权,即使它租借了所有权,它也只享有使用权(这跟租房子是一个道理)。
如果尝试利用租借来的权利来修改数据会被阻止:
fn main() {
let s1 = String::from("run");
let s2 = &s1;
println!("{}", s2);
s2.push_str("oob"); // 错误,禁止修改租借的值
println!("{}", s2);
}
这段程序中 s2 尝试修改 s1 的值被阻止,租借的所有权不能修改所有者的值。
fn main() {
let mut s1 = String::from("run");
// s1 是可变的
let s2 = &mut s1;
// s2 是可变的引用
s2.push_str("oob");
println!("{}", s2);
}
这段程序就没有问题了。我们用 &mut 修饰可变的引用类型。
可变引用与不可变引用相比除了权限不同以外,可变引用不允许多重引用,但不可变引用可以
Rust 对可变引用的这种设计主要出于对并发状态下发生数据访问碰撞的考虑,在编译阶段就避免了这种事情的发生。
由于发生数据访问碰撞的必要条件之一是数据被至少一个使用者写且同时被至少一个其他使用者读或写,所以在一个值被可变引用时不允许再次被任何引用。
垂悬引用
如果放在有指针概念的编程语言里它就指的是那种没有实际指向一个真正能访问的数据的指针(注意,不一定是空指针,还有可能是已经释放的资源)
"垂悬引用"在 Rust 语言里不允许出现,如果有,编译器会发现它。
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
很显然,伴随着 dangle 函数的结束,其局部变量的值本身没有被当作返回值,被释放了。但它的引用却被返回,这个引用所指向的值已经不能确定的存在,故不允许其出现。
切片
Rust 中的字符串类型实质上记录了字符在内存中的起始位置和其长度,我们暂时了解到这一点)
s = "123456"
sli = s[0:5]
fn main() {
let s = String::from("123456");
println!("{}", &s[0..5]);
}
v = [1,2,3]
sli = v[1:]
print(sli)
fn main() {
let v = [1, 2, 3];
let sli = &v[1..];
for idx in sli.iter() {
print!("{idx}");
}
}
struct
如果你常用 C/C++,请记住在 Rust 里 struct 语句仅用来定义,不能声明实例,结尾不需要 ; 符号,而且每个字段定义之后用 , 分隔。
struct Site {
domain: String,
name: String,
nation: String,
found: u32
}
实例化
let runoob = Site {
domain: String::from("www.runoob.com"),
name: String::from("RUNOOB"),
nation: String::from("China"),
found: 2013
};
如果正在实例化的结构体有字段名称和现存变量名称一样的,可以简化书写:
let domain = String::from("www.runoob.com");
let name = String::from("RUNOOB");
let runoob = Site {
domain, // 等同于 domain : domain,
name, // 等同于 name : name,
nation: String::from("China"),
traffic: 2013
};
有这样一种情况:你想要新建一个结构体的实例,其中大部分属性需要被设置成与现存的一个结构体属性一样,仅需更改其中的一两个字段的值,可以使用结构体更新语法:
let site = Site {
domain: String::from("www.runoob.com"),
name: String::from("RUNOOB"),
..runoob
};
注意:…runoob 后面不可以有逗号。这种语法不允许一成不变的复制另一个结构体实例,意思就是说至少重新设定一个字段的值才能引用其他实例的值。
元组结构体
方法
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1's area is {}", rect1.area());
}
结构体函数
static ?
这种函数不依赖实例,但是使用它需要声明是在哪个 impl 块中的。
一直使用的 String::from 函数就是一个"关联函数"。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn create(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
}
fn main() {
let rect = Rectangle::create(30, 50);
println!("{:?}", rect);
}
后面的部分呢,生命周期,trait,泛型呢
回复删除抱歉,笔者不再学习rust
回复删除