常量
- 声明常量使用const, 声明变量使用let, 如果想要一个变量可变 加入关键字mut, 对于常量的命名规范: 大写加下划线,常量的声明周期是在整个程序的声明周期内都是有效的
- 变量隐藏
fn main(){
let x = 5;
let x = x + 1;
{
let x = x * 2
}
}
如上所示定义一个与之前同民的新变量,表示第一个变量被第二个变量shadowing
区别:
1. 能够没有使用mut 标记变量重新赋值 不会有编译错误,但是返回变量还是不可变
2. 再一次使用let后 实际创建一个新变量,与mut不同修改变量的值和类型
let space = " ";
let space = space.len(); // 但是如果变量使用 mut 修饰 会在此报错 修改变量类型
数据类型推断
Rust是静态语言必须在编译时候 知道所有变量类型, rust中两类数据自己标量,复合
- 对于 字符串转换成数字 使用 parse进行解析 和 JAVA类似
- 元组与数组最大区别在于:元组不需要保证每一个元素的类型相同, 数组是属于栈上分配固定大小的单个内存模块
let a:[i32; 5] = [1,2,3,4,5];
变量名称a的数组包含5个元素
let a = [3; 5] // 有5个元素初始化值为3
函数
RUST 代码中 使用fn 关键字 使用 snake_case 风格,小写_下划线
R语言中 函数签名中必须要声明每个函数的类型.
语句 Vs 表达式
语句: 执行一些操作但是没有返回值, 表达式: 计算一些结果值
函数调用,宏调用,和{} 都是属于表达式
let y = {
let x = 3;
x + 1 // 注意没有;
};
通过判断是否有;为标准,有;是语句,无分号是表达式,比如上面表达式无分号但是 可以进行赋值
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1 // 注意没有分号 是一个表达式 有返回值,如果增加;赋值语句无返回值
}
& 引用
& 由于所有权的问题,在变量被赋值后原有变量的所有权会被消失,这样导致一个问题我们无法重复使用该变量, 如何解决变量在返回给调用函数后,我们还能够使用该变量, Rust语言提出一种 &引用的概念,表示我们只是借用但是不使用其所有权
在函数传参数时候 我们将一个对象的引用作为参数,避免使用所有权,因此当引用停止使用后,其指向值也不会被丢弃
引用是不能修改的,如果想要修改请使用可变引用 &mut a
有引用有解引用 使用运算符合是 *
fn main() {
let s = String::from("hello world");
let word = first_word(&s);
println!("{}", word);
}
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..] // 找寻不到返回整个字符串没有; 直接返回
}
结构体
类似于C语言中结构体,JAVA中类对象一样, 在面向对象过程中有关联的变量组合到一起
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let mut user2 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user2.email = String::from("anotheremail@example.com");
}
- 没有任何参数的类单元结构体
作用: 某个类型上实现特性但是不关注类型中存储的数据
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
- 结构体程序
fn main() {
let width1 = 30;
let height1 = 50;
println!(
"The area of the rectangle is {} square pixels.",
area(width1, height1)
);
}
fn area(width: u32, height: u32) -> u32 {
width * height
}
// rust 在设计时候 高内聚,低耦合,明显对于面积计算 width, hegiht 属于让此代码更加具有明确性和增加可读性的应用, 在area 签名上
// 变种一: 使用元组,虽然能够减少参数,但是有个问题无法知道参数的意义,容易混淆造成值的错误,在代码中没有表达数据的意图
// 变种2:结果体赋予数据更多意义
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect = Rectangle {
width: 30,
height: 50,
};
println!("area of rectangle is {}", area(& rect))
}
fn area(rectangle &Rectangle) ->u32 {
rectangle.width * rectangle*height // 表达式结果返回
}
- trait派生增加使用功能
rust 没有JAVA中继承概念,使用trait派生方式进行方法组合
对于结构体要向先生 println!,- 外部属性: #[derive(Debug)]
- {} ->{:?} 以DEBUG方式展示
**增加属性来派生 Debug trait ,并使用调试格式打印Rectangle 实例删除线格式**
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {}", rect1); // 报错 对于结构体无法使用Display个数
}
src\main.rs:12:29
|
12 | println!("rect1 is {}", rect1);
| ^^^^^ `Rectangle` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `Rectangle`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
-------
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {:?}", rect1); // 更大的结构体使用 {:#?}
}
- 使用dbg! 宏 dbg!宏将输出结果打印到stderr, println! 输出结果到stdout, dbg! 接收一个表达式的所有权
println!("rect1 is {:?}", rect1); // 通过stdout 标准输出控制流
dbg!(&rect1); // 输出结果到stderr 标准错误输出控制流
后续对trait扩展 ,可以写自定义的trait
方法
方法和函数类似, 使用fn 关键字和名称什么, Python内部中CLass内的方法,函数 在Class外表示函数,方法前面带有self 标识, 第一个参数总是self, 代码使用该方法的结构体实例
#[derive(Debug)]
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 is {}", rect1.area()); // 通过stdout 标准输出控制流
}
类似JAVA中getters 一样 与字段同名方法将被定义值返回字段中值,
C/C++ 中 ., -> 调用方法, .直接在对象上调用方法,-> 在一个对象的指针上调用方法,需要解引用指针 object是一个指针 object->someting() 等价于(*object).something()
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
// can_hold方法 一个长方体是否能够包含另外一个长方体
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
- 关联函数
** 不是方法的关联函数叫构造函数,:: 返回结构体新实例,类似JAVA中构造函数**
所有在impl中定义的函数叫关联函数,如果定义一个不以self为第一个参数的关联函数,让其并不作用一个结果体的实例, 叫做不是方法的关联函数, 作用: 返回一个结构体的新实例构造函数 ,类似于JAVA的构造函数, 外部使用:: 调用这个构造函数
impl Rectangle {
fn square(size: u32) -> Rectangle { // 第一个参数不是self
Rectangle {
width: size,
height: size,
}
}
}
- 多个impl 块: 作用 主要运用于泛型和trait 派生上,有多个impl, 结构类似组合到一个impl, 派生在另外一个impl 块中
- 总结:
结构体让你可以创建出在你的领域中有意义的自定义类型。通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。在 impl 块中,你可以定义与你的类型相关联的函数,而方法是一种相关联的函数,让你指定结构体的实例所具有的行为
结构体并不是创造自定义类型的唯一方法,枚举也可以创建自定义方法
枚举
使用场景在于使用过程遇到所有的IP类型 通过枚举出所有的可能值
enum IpAddrKind {
V4,
V6
}
let four = IpAddrKind::V4;
fn main() {
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
}
-- 根据简洁方式:
fn main() {
enum IpAddrKind {
V4(String),
V6(String),
}
let home = IpAddrKind::V4(String::from("127.0.0.1"));
}
将任意的类型的数据放入到枚举成员中,比如字符串,数字类型。或者其他枚举
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
- 枚举中定义方法
impl Message{
fn call(&self){
}
}
let m = Message::Write(String::from("hello"));
m.call();
- 介绍标准库中一个Option 枚举, 一个非常普通场景 一个值要么有值或者没有值, NULL值问题 当你尝试像一个非空值一样使用空值 会出现空指针异常,rust中没有空值,但是有一个有和无的概念
有点类似于JAVA8 中Option这个泛型工具类
return Optional.ofNullable(user)
.map(u-> u.getAddress())
.map(a->a.getCity())
.orElseThrow(()->new Exception("取指错误"));
match匹配
rust 有一个叫做 match的的控制流运算符,允许将一个值与一系列的模式向比较得到匹配的执行向队员代码,match来源于模式的表现力以及编译器。
match 与if最大区别在于match匹配任何类型,if只能是boolean, => 将模式与代码分开
注意: match 会按照顺序依次匹配,类似JAVA switch
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
- 与rust 核心代码中 Option
fn main() {
// 编写代码获取Option<i32>,其中包含值+1,无返回空值
fn plus_one(x: Option<i32>) -> Option<i32>{
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
}
other 表示其他类型 类似JAVA中switch default
- 总结: match 结合枚举类一起适用于很多场景
- if let 简单控制流
if let 语法让我们不再冗长结合 if 和 let, if let 是match 一个语法糖
当值匹配某一模块执行代码,但是忽略其他模块, 不能使用match 的穷尽性检查
let mut count = 0;
match coin {
Coin::Quarter(state) => println!("State quarter from {:?}!", state),
_ => count += 1, // 其他格式全部加1
}
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
} else {
count += 1;
}