基础语法
🦀

基础语法

创建时间
Oct 20, 2024 11:58 AM
上次编辑时间
Last updated November 3, 2024
标签
rust
基础
入门

Cargo

  • Rust的包管理工具。
cargo new hello // 新建项目 cargo new hello --vcs none // 不创建git仓 cargo clean // 清理编译后的输出 cargo run // 跑项目 cargo test // 单元测试

单元测试

  • #[test] 类似 Java中的 @Test 注解。
#[test] fn test_method() { assert_eq!(target_method(1, 2), 3); } fn target_method(i: isize, j: isize) -> isize { i + j }
// ouput ~/project/rust_start/demo$ cargo test Finished `test` profile [unoptimized + debuginfo] target(s) in 0.01s Running unittests src/main.rs (target/debug/deps/demo-7c5256d3da2216fe) running 1 test test test_method ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

assert!

  • assert! 无论什么时间都会运行,而 debug_assert! 在发布版本中会被跳过。
  • 类似的函数还有比如 assert_eq!

语句和表达式

  • Rust 中的语法可以分成两大类:语句 (Statement )和表达式 (Expression )。.
    • 语句是指要执行的一些操作和产生副作用的表达式。语句又分为两种:声明语句 ( Declaration statement )和表达式语句 ( Expression statement ).
      • 声明语句,用于声明各种语言项(Item),包括声明变量、静态变量、常量、结构体、函数等,以及通过extern和use关键字引入包和模块等。
      • 表达式语句,特指以分号结尾的表达式。此类表达式求值结果将会被舍弃,并总是返回单元类型()
    • 表达式主要用于计算求值。

Debug

  • 使用Debug trait可实现对内容的自动打印。
  • 仅有一些类型提供了自动实现,比如 std 库中的类型。所有其他类型都必须手动实现。
  • 如下代码 #[derive(Debug)] 会自动探测类型,输出其内容,但是对于复杂类型可实现 Display 来实现自定义的结果输出。
  • trait 的理解类似于 Java中的 interface。
#[derive(Debug)] struct MyStruct { name: String } fn main() { let my_struct = MyStruct { name: "ohBug".to_string() }; println!("{:#?}", my_struct); } // output MyStruct { name: "ohBug", }

Display

  • 虽然debug trait 可实现对结构体的内容输出,那也只是默认的,对于自定义输出,需要实现Display 就可以。
use std::fmt; struct MyStruct { name: String, } impl fmt::Display for MyStruct { // 下面这段方法签名从DisPlay的源代码注释中可以翻看到。 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "my name is {}", self.name) } } fn main() { let my_struct = MyStruct { name: "ohBug".to_string(), }; println!("{}", my_struct); } // ouput : my name is ohBug

变量

  • 所有对于变量的初始化操作都理解为绑定而非赋值,因为默认情况下定义的变量是不可变的。
  • 对于不用而又定义了的变量可以在变量前加_,否则会有warning。
  • Rust会为每个crate都自动引入标准库模块,除非使用#[no_std]属性明确指定了不需要标准库。
fn main { let a = 4; let _b = 6; }
warning: unused variable: `a` --> src/main.rs:2:9 | 2 | let a = 4; | ^ help: if this is intentional, prefix it with an underscore: `_a` | = note: `#[warn(unused_variables)]` on by default warning: `demo` (bin "demo") generated 1 warning
  • 变量遮盖,shadowing。
fn main() { let a = 5; let a = 6; println!("{}", a) }
warning: unused variable: `a` --> src/main.rs:2:9 | 2 | let a = 5; | ^ help: if this is intentional, prefix it with an underscore: `_a` | = note: `#[warn(unused_variables)]` on by default warning: `demo` (bin "demo") generated 1 warning Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.97s Running `target/debug/demo` 6

数值类型

  • 默认整数i32,默认浮点类型是 f64,usize与isize是机器多少位它就多少位。
notion image
notion image
  • 整数溢出处理:当溢出时在debug模式下编译运行的rust会进行检查,如果溢出则panic,但是在release下则不会报错了。溢出大的处理就是采取循环进位,比如在 u8 的情况下,256 变成 0,257 变成 1。显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:
    • 使用 wrapping_* 方法在所有模式下都按照补码循环溢出规则处理
    • 如果使用 checked_* 方法时发生溢出,则返回 None
    • 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
    • 使用 saturating_* 方法,可以限定计算后的结果不超过目标类型的最大值或低于最小值
fn main() { let a: u8 = 255; println!("a= {}", a); // panicked: attempt to add with overflow // println!("a= {}", a + 2); let a: u8 = 255; println!("a= {:#?}", a.wrapping_add(2)); println!("a= {:#?}", a.checked_add(2)); println!("a= {:#?}", a.overflowing_add(2)); let (num, is_overflow) = a.overflowing_add(2); println!("num= {:#?}, is_overflow= {}", num, is_overflow); println!("a= {:#?}", a.saturating_add(2)); }
a= 255 a= 1 a= None a= ( 1, true, ) num= 1, is_overflow= true a= 255
  • 浮点数比较, abs() 取决于精度需求是多少。而不可以用等于直接判断两个浮点数大小。
fn main() { let a = 0.1; let b = 0.2; let c: f64 = 0.3; let is_eq = a + b == c; println!("is_eq = {}", is_eq); // 若a或b或c不指定为f64则panic,因为assert不能分辨计算后的类型 let is_eq = (c - a - b).abs() < 0.0001; println!("is_eq = {}", is_eq); }
is_eq = false is_eq = true

序列循环

fn main() { for i in 1..4 { print!("{}", i); } println!(""); for i in 1..=4 { print!("{}", i); } // 如果此局部变量在本块中并不会用到,可以简单使用—个下画线来代替此变量 for _ in 0..3 { println!("hello") } // 不推荐使用这种方式,因为会带来额外的运行时边界检查 let connections = [0, 1, 2, 3, 4, 5]; for i in 0..connections.len() { println!("{}", connections[i]) } // loop 可以作为无限循环使用,break可打断。 // 跳转标签 'outter: for i in 0.. { for j in 0.. { for k in 0.. { if i + j + k % 100 == 0 { break 'outter; } } } } println!("outter break"); // 从循环中获取一个返回值 let a = loop { break 123; }; }

数组

  • 分为静态数组和可变数组。
fn main() { // 为固定尺⼨的数据类型是可以直接放栈上的,创建和回收都⽐在堆上动态分配的动态数组性能要好 let arr:[i64; 4] = [1, 2, 3, 4]; println!("arr {:?}", arr); let arr = [1, 2, 3, 4]; println!("arr {:?}", arr); for (count, elem) in arr.iter().enumerate() { println!("index :{}: value {}", count, elem); } for elem in arr { println!("elem {}", elem); } for i in 0..arr.len() { println!("arr[{}] {}", i, arr[i]); } let mut dynamic_arr: Vec<i32> = Vec::new(); dynamic_arr.push(1); println!("dynamic_arr {:?}", dynamic_arr); let dynamic_arr = vec![1, 2, 3, 4]; println!("dynamic_arr {:?}", dynamic_arr); for (count, elem) in dynamic_arr.iter().enumerate() { println!("index :{}: value {}", count, elem); } // 注意 若是 elem in dynamic_arr 这种形式,则后续的for则无法编译过,因为这种形式所有权发生了变化。 for elem in &dynamic_arr { println!("elem {}", elem); } for i in 0..dynamic_arr.len() { println!("arr[{}] {}", i, arr[i]); } } // arr [1, 2, 3, 4] // arr [1, 2, 3, 4] // index :0: value 1 // index :1: value 2 // index :2: value 3 // index :3: value 4 // elem 1 // elem 2 // elem 3 // elem 4 // arr[0] 1 // arr[1] 2 // arr[2] 3 // arr[3] 4 // dynamic_arr [1] // dynamic_arr [1, 2, 3, 4] // index :0: value 1 // index :1: value 2 // index :2: value 3 // index :3: value 4 // elem 1 // elem 2 // elem 3 // elem 4 // arr[0] 1 // arr[1] 2 // arr[2] 3 // arr[3] 4

函数

  • 如果一个函数体以没有尾随着分号的表达式结尾,那么这个表达式就是函数的返回值。
关键字 方法名 (形参名: 形参类型) -> 返回值类型 { ... } fn add(i: i32, j: i32) -> i32 { i + j }
  • 函数作为参数。
fn main() { let result = math(add, 1, 2); println!("{}", result); let result = math(pro, 2, 2); println!("{}", result); } fn math(op: fn(isize, isize) -> isize, i: isize, j: isize) -> isize { op(i, j) } fn add(i: isize, j: isize) -> isize { i + j } fn pro(i: isize, j: isize) -> isize { i * j }
  • 函数作为返回值。
fn main() { // 返回的是对方法引用的指针 let method = product(); // () 表示调用,method仅为对方法的指针。 let log = method(); println!("{}", log); } fn product() -> fn() -> String { return_log } fn return_log() -> String { "log info : xxx".to_string() }

闭包

  • 闭包与函数的⼀个显著不同就是,闭包可以捕获函数中的局部变量为我所⽤,⽽函数不⾏。
fn main() { let outter_var = 12; // 三种方式都可以 // let pkg = |x:i32| -> i32 {x + outter_var}; // let pkg = |x| x + outter_var ; let pkg = |x| {x + outter_var}; println!("{:?}", pkg(8)) } //output :20