0%

rust入门(一)

听闻rust大名已久,正巧赶上2023 秋冬季开源操作系统训练营,就来学习啦。

1
2
3
fn main() {
println!("Hello,world!");
}

从最经典的打印hello world开始,可以看到和c语言比较像,都是在main函数中执行,不同的是不需要头文件,而且打印方法非常的多元。

println!

这样,从这个方法,可以看出 {} 相当于c语言中的占位符 %d

1
2
3
4
fn main() {
let s="world";
println!("Hello {} ",s);
}

这样

1
2
3
4
fn main() {
let s="world";
println!("Hello {s} ");
}

都是可以正常打印出Hello world的。

这里提一嘴rust的编译,非常的简单,在终端源码目录下运行,即可得到一个可执行文件

1
rustc main.rs

变量与可变性

!!!Rust的变量是默认不可变的 这就非常的反直觉,不过这会使得程序变得更加安全,变量用let来声明

1
2
3
4
5
let x=5

x=6

x=x+1

下面两条试图改变变量x的语句都是不合法的,要想使变量真正可变,需要在创建时加上一个mut (mutable,可变的)

1
2
3
let mut x =5;
x=6;
x=x+1;

常量

常量总是不可变的,使用const而不是let声明

1
const CON: u32=3;

在这条语句中,const表示声明一个常量,CON是名称,u32是常量的类型即无符号32位整数,常量必须是在编译阶段已经确定的

1
2
3
const CON: u32=3+3+3//编译通过

const CON: u32=x+1//编译不通过

常量的命名规则,单词全大写并且用_连接。

基本数据类型

元组

和c语言的结构体很像,可以存储不同类型的值,引用方面比较奇怪。

元组的引用方法

方法一

1
2
3
4
5
6
fn main(){
let tup=(500,6.4,1);
let (_x,y,_z)=tup; //这一步把元组里的三个数据分别赋值给了x,y,z,在x前加上_告诉编译器x是有意不使用的,这样就不会发出warning
println!("y={y}");
}
//y=6.4

方法二 索引

1
2
3
4
5
fn main(){
let tup=(500,6.4,1);
println!("y={}",tup.0);
}
//y=500 索引下标从零开始

数组

1
2
let a:[i32;5]={1,2,3,4,5}; //a是名字,[]里是类型 有符号32位整数 分号后面的5表示长度为5
let a={3;5}//申请一个长度为5全部填充3的数组

输出数组,通过这个输出可以看出print之后是默认换行的

1
2
3
4
5
6
fn main(){
let a:[i32; 5]= [1,2,3,4,5];
for i in a {
println!("{}",i);
}
}

函数

命名规则单词全部小写,函数的声明可以在程序的任何位置

1
2
3
4
5
6
7
8
fn main(){
aaa();
}

fn aaa(){
println!("xxx");
}
//xxx

含有参数的函数

1
2
3
4
5
6
7
fn main(){
aaa(5);
}
fn aaa(x:i32){
println!("x={}",x);
}
//x=5

含有返回值的函数,返回值需要显示地表示出来

1
2
3
4
5
6
7
8
9
10
fn main(){
let x:i32=aaa(5);
println!("aaa return {x}");
}
fn aaa(x:i32)->i32{
println!("x={}",x);
6
}
//x=5
//aaa return 6

对,我没有写错,最后一行6的后面就是没有分号,我们可以这样理解:最后一行6是一个常量表达式,它的值等于整数6。在这个函数中,它被用作返回值。

  • 表达式:先这么记,代码没有分号则是一个表达式,表达式会返回值,可以用来赋值
  • 语句 let 1:i32=5; 这就是一个语句,无返回值。

if

需要注意的是if后面的判断表达式返回值必须是bool值。

loop

就然还有loop,我只在汇编中接触过loop,可以从loop中返回一个值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main(){
let mut a:i32=0;
let x=loop{
a+=1;
println!("hello");
if a>3{
break a; //跳出循环返回a
}
};
println!("x={x}");
}
//hello
//hello
//hello
//hello
//x=4

loop中break和continue的用法和c语言类似。

嵌套循环

作用是精确的退出多层循环。

while和for循环

所有权概念

这里存在疑问!!!!!!!!!!!!!!!!!!

1
2
3
4
5
6
7
8
fn main(){
let x=123;
let y=x;
println!("x={}",x);
println!("y={}",y);
}
//x=123
//y=123

按照对所有权的介绍,打印x的时候x应该已经弃用了。

变量作用域与变量隐藏

**{}**是作用域,

1
2
3
4
5
6
7
8
9
10
11
fn main(){
let x=122;
let x=x+1; //变量隐藏
{
let x=124; //x的作用域
println!("x={}",x); //x的作用域
}
println!("x={}",x);
}
//x=124
//x=123

引用与借用

s和s1的关系如下图所示。&是引用符号

首先s不是指向hello字符串的,所以不存在与s1竞争所有权的问题,其次离开s的作用域后s丢弃,s1以及hello字符串不受影响。

rustlings记录

关于if

1
2
3
4
5
6
7
8
9
10
pub fn bigger(a: i32, b: i32) -> i32 {
if a>b{
a
}
if a<b{
b
}
}
//note: `if` expressions without `else` evaluate to `()`
//help: consider adding an `else` block that evaluates to the expected type

if语句必须跟一个else语句

slice 切片

1
2
3
4
5
6
7
fn slice_out_of_array() {
let a = [1, 2, 3, 4, 5];

let nice_slice = &a[1..4]; //从下标 1 至下标 4-1

assert_eq!([2, 3, 4], nice_slice)
}

clone和借用

clone

1
2
3
4
5
let s1 = String::from("Hello");
let s2 = s1.clone();

println!("s1: {}", s1); // 此处仍然可以使用s1
println!("s2: {}", s2); // 输出 "Hello"

clone是用于创建数据的完全拷贝,包括堆上的数据。它会复制原始数据,生成一个新的独立副本,两者之间没有关联。clone适用于需要独立拥有数据的情况,确保对数据的修改不会影响原始数据。

借用

1
2
3
4
5
6
fn print_length(s: &str) {
println!("Length: {}", s.len());
}

let s = String::from("Hello");
print_length(&s); // 传递s的引用给函数,不会转移所有权

借用(borrowing)则是一种不拥有数据但可以临时访问的机制。通过借用,可以将数据的引用传递给其他代码,而无需转移所有权。在使用借用时,原始数据保持不变,可以有多个不可变引用同时存在,但只能有一个可变引用。这样可以在编译时避免数据竞争和并发问题。