rus语法 2
泛型
接口约束也可以是空接口
struct Empty;
struct Null;
/* impl Display for Empty, Null */
trait Drop<T> {
fn drop(self, _: T);
}
impl<T: Display, U> Drop for U // 此处 T 被加上了 Display 接口的约束
where // where 用于让接口约束更美观
U: Display + Debug, // 组合多个不同接口
{
fn drop(self, _: T) {} // 此处 self 和参数都 move 了过来,函数返回时变量死亡
}
fn main() {
let empty = Empty;
let null = Null;
empty.drop(null); // empty 和 null 都move到方法内,之后死亡
}
不得不用 where 进行约束的场景:
impl<T> PrintInOption for T
where
Option<T>: Debug // 因为用于打印的是 Option<T>,所以无法通过正常语法约束T
{
fn print_in_option(self) {
println!("{:?}", Some(self));
}
}
使用类型关联使得容器中某种些变量的类型由其他变量决定
struct Container(i32, i32);
trait Contains {
// Define generic types here which methods will be able to utilize.
type A;
type B;
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
fn first(&self) -> i32;
fn last(&self) -> i32;
}
impl Contains for Container {
type A = i32;
type B = i32;
fn contains(&self, number_1: &Self::A, number_2: &Self::B) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
fn first(&self) -> i32 { self.0 }
fn last(&self) -> i32 { self.1 }
}
// 不使用类型绑定的写法:fn difference<A, B, C>(container: &C) -> i32 where C: Contains<A, B> { ... }
fn difference<C: Contains>(container: &C) -> i32 {
container.last() - container.first()
}
作用域规则
struct ToDrop;
// 析构函数,非必须,销毁时自动调用
impl Drop for ToDrop {
fn drop(&mut self) {
println!("ToDrop is being dropped");
}
}
fn main() {
let x = ToDrop;
println!("Made a ToDrop!");
}
所有权
所有资源只有一个拥有者,但不是所有变量都有资源。例如 let x = y
foo(x)
资源所有权发生转移,即 move,解决了悬挂指针的问题。
在 move 时 Mutability 可以改变
fn destory_box(c: Box<i32>) {
println!("Destroying a box contains {}", c);
}
fn main() {
let x = 5u32; // stack alloc
let y = x; // u32 have Copy trait
println!("{} {}", x, y); // 引用
let a = Box::new(5i32);
let b = a; // move
destory_box(b);
}
let Person {
name, // move
ref age // borrow
} = Person {
name: String::from("Alice"),
age: Box::new(20),
};
借用
函数传参时通过 &T
得到参数的引用,&mut T
为可变引用。
- 在不可变引用使用期间,元对象不可被可变引用
- 同一时间段只能有一个可变引用
- 在最后一个可变引用出现的语句之后,此可变引用生命结束
fn main() {
let mut i = 1;
let i1 = &i;
let ref i11 = i; // 使用 ref 声明引用
let i2 = &mut i;
// let i3 = &mut i; // 此处会报错,因为多个可变引用冲突
// i = 2; // 此处报错,因为i被借用
println!("{}", i2); // 可变引用i2最后一次出现
let i3 = &mut i;
println!("{}", i3);
// println!("{}", i1); // 如果有这个语句,前面两个可变引用也出错
}
// 在 destructuring 中使用ref指定部分字段为引用
let Point {
x: ref ref_to_x,
y: _
} = point;
生命周期
// Lifetimes are annotated below with lines denoting the creation
// and destruction of each variable.
// `i` has the longest lifetime because its scope entirely encloses
// both `borrow1` and `borrow2`. The duration of `borrow1` compared
// to `borrow2` is irrelevant since they are disjoint.
fn main() {
let i = 3; // Lifetime for `i` starts. ────────────────┐
// │
{ // │
let borrow1 = &i; // `borrow1` lifetime starts. ──┐│
// ││
println!("borrow1: {}", borrow1); // ││
} // `borrow1` ends. ─────────────────────────────────┘│
// │
// │
{ // │
let borrow2 = &i; // `borrow2` lifetime starts. ──┐│
// ││
println!("borrow2: {}", borrow2); // ││
} // `borrow2` ends. ─────────────────────────────────┘│
// │
} // Lifetime ends. ─────────────────────────────────────┘
有生命周期显式注释的函数签名
在函数中使用泛型和符号 'a
'b
'c
... 限制引用的生命周期
// `print_refs` takes two references to `i32` which have different
// lifetimes `'a` and `'b`. These two lifetimes must both be at
// least as long as the function `print_refs`.
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
println!("x is {} and y is {}", x, y);
}
// A function which takes no arguments, but has a lifetime parameter `'a`.
fn failed_borrow<'a>() {
let _x = 12;
// ERROR: `_x` does not live long enough
let y: &'a i32 = &_x;
// Attempting to use the lifetime `'a` as an explicit type annotation
// inside the function will fail because the lifetime of `&_x` is shorter
// than that of `y`. A short lifetime cannot be coerced into a longer one.
}
fn main() {
// Create variables to be borrowed below.
let (four, nine) = (4, 9);
// Borrows (`&`) of both variables are passed into the function.
print_refs(&four, &nine);
// Any input which is borrowed must outlive the borrower.
// In other words, the lifetime of `four` and `nine` must
// be longer than that of `print_refs`.
failed_borrow();
// `failed_borrow` contains no references to force `'a` to be
// longer than the lifetime of the function, but `'a` is longer.
// Because the lifetime is never constrained, it defaults to `'static`.
}
// One input reference with lifetime `'a` which must live 输入引用的生命至少与函数一样长
// at least as long as the function.
// Mutable references are possible with lifetimes as well.
fn add_one<'a>(x: &'a mut i32) {
*x += 1;
}
// Multiple elements with different lifetimes. In this case, it
// would be fine for both to have the same lifetime `'a`, but
// in more complex cases, different lifetimes may be required.
fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) {
println!("`print_multi`: x is {}, y is {}", x, y);
}
// Returning references that have been passed in is acceptable.
// However, the correct lifetime must be returned.
fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x }
// 注意返回值的生命周期必须 与输入生命周期相同 或 为static
fn invalid_output<'a>() -> &'a String { &String::from("foo") }
// The above is invalid: `'a` must live longer than the function.
// Here, `&String::from("foo")` would create a `String`, followed by a
// reference. Then the data is dropped upon exiting the scope, leaving
// a reference to invalid data to be returned.
结构体和方法都可以有生命周期标注
struct NamedBorrowed<'a> {
x: &'a i32,
y: &'a i32,
}
impl<'a> Default for NamedBorrowed<'a> {
fn default() -> Self {
Self {
x: &10,
}
}
}
// method 与 function 一样,都可使用生命周期
struct Owner(i32);
impl Owner {
// Annotate lifetimes as in a standalone function.
fn add_one<'a>(&'a mut self) { self.0 += 1; }
fn print<'a>(&'a self) {
println!("`print`: {}", self.0);
}
}
泛型的生命周期约束
泛型要满足指定接口且生命周期要长于指定符号
fn print_ref<'a, T>(t: &'a T)
where
T: Debug + 'a
{
println!("`print_ref`: t is {:?}", t);
}
强制转换
生命周期存在差异时强制转换为较短的情况
// 输入变量生命周期不同,a 成为较短的那个
fn multiply<'a>(first: &'a i32, second: &'a i32) -> i32 {
first * second
}
// a 至少和 b 一样长,返回的生命周期被转换为 b
fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 {
first
}
fn main() {
let first = 2; // Longer lifetime
{
let second = 3; // Shorter lifetime
println!("The product is {}", multiply(&first, &second));
println!("{} is the first", choose_first(&first, &second));
};
}
Static 静态生命周期
'static 对应变量存储在二进制的只读空间,有2种创建方法:
- 使用
static
关键字声明 - 创建
&'static str
类型的字符串
static NUM:i32 = 1;
let static_string = "I'm in read-only memory";
在签名中可以使用 'static 约束
fn print_it( input: impl Debug + 'static ) {
println!( "'static value passed in is: {:?}", input );
}
fn main() {
// i is owned and contains no references, thus it's 'static:
let i = 5;
print_it(i);
// Oops!!! &i only has the lifetime defined by the scope of main(), so it's not 'static:
print_it(&i);
}
生命周期是一定存在的,只是大多数情况下可以省略并由编译器推断
接口
某些简单的内置接口可通过 #[derive]
实现,也可手动实现更复杂版本
- Comparison traits:
Eq
,PartialEq
,Ord
,PartialOrd
. Clone
, to create T from &T via a copy.Copy
, to give a type 'copy semantics' instead of 'move semantics'.Hash
, to compute a hash from &T.Default
, to create an empty instance of a data type.Debug
, to format a value using the {:?} formatter.
使用 dyn
返回接口
在 rust 中返回值大小需要固定大小,无法直接返回一个 trait,可以返回一个指向接口的 Box 指针。但 rust 要求尽可能的显示声明堆内存分配,所以要加上 dyn
fn fun() -> Box<dyn Display> {
Box::new(A_Struct_impl_Display{})
}
操作符重载
实现 std::ops
下的接口,如 std::ops::Add
std::ops::Sub
core::ops - Rust (rust-lang.org) doc.rust-lang.org/core/ops/
use std::ops::{Add, Mul};
#[derive(Debug)]
struct Foo;
impl Add for Foo {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
rhs
}
}
#[derive(Debug)]
struct Bar;
impl Mul<Foo> for Bar { // 注意泛型,默认 Self
type Output = Foo;
fn mul(self, rhs: Foo) -> Self::Output {
rhs
}
}
fn main() {
let foo1 = Foo{};
let foo2 = Foo{};
let foo3 = Foo{};
let bar = Bar{};
println!("{:?}", foo1 + foo2);
println!("{:?}", bar * foo3);
}
Drop 接口
struct Droppable {
}
impl Drop for Droppable {
fn drop(&mut self) {
// 销毁时调用,释放资源。
// 按照对象生成的相反顺序销毁,类似出栈
}
}
迭代器接口
struct Fibonacci {
curr: u32,
next: u32,
}
impl Fibonacci {
fn new() -> Fibonacci {
Fibonacci { curr: 1, next: 1 }
}
}
impl Iterator for Fibonacci {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
let current = self.curr;
self.curr = self.next;
self.next = self.curr + current;
Some(current)
}
}
fn main() {
let fib = Fibonacci::new();
for i in fib.skip(2).take(5) {
// for 循环中自动调用 .into_iter() 方法
println!("> {}", i); // 2 3 5 8 13
}
for i in 0..3 {}
}
traits 作为输入输出
fn f<'a>(r : &'a impl Display) -> impl Iterator + 'a {
// 直接使用 imp traits 省略泛型
}
复制
- Copy 简单的按位拷贝
- Clone 深拷贝
// 出现Copy时必须有Clone
#[derive(Debug, Clone, Copy)]
struct Unit;
// 可拷贝 Box 指针内部数据
#[derive(Clone, Debug)]
struct Pair(Box<i32>, Box<i32>);
接口组合
rust 中没有“继承”
trait Obj {}
trait Named: Obj {
fn name(&self) -> String;
}
trait Animal {
fn run(self);
}
trait Dog: Named + Animal {}
fn f(d: &dyn Dog) {
d.name();
d.run();
}
接口方法冲突
trait A {
fn fun(&self);
}
trait B {
fn fun(&self);
}
struct S;
impl A for S {
fn fun(&self) {}
}
impl B for S {
fn fun(&self) {}
}
fn main() {
let s = S {};
<S as A>::fun(&s);
<S as B>::fun(&s);
}
错误处理
panic!();
用于不可恢复错误Option
用于接收缺少值的情况Result
用于让调用者解决错误
panic
fn fun() {
panic!("严重错误");
}
条件编译
fn drink(s: &str) {
if cfg!(panic="abort"){
println!("");
} else {
println!("");
}
}
#[cfg(panic = "abort")]
fn ah(){ println!("xxx");}
#[cfg(not(panic="abort"))]
fn ah(){ println!("yyy");}
fn drink(){
ah();
}
rustc lemonade.rs -C panic=abort
指定编译条件
Option
pub enum Option<T> {
/// No value.
None,
/// Some value of type `T`.
Some(T),
}
可使用 match 分支或 unwrap 解决
使用 ?
Option变量后接上一个 ? 如果是Some则得到内部值,否则终止函数并返回 None
map map_or map_or_else 函数式编程
简化 match 分支语句
option.map(move |v| {....});
option.map_or(0, move |v| {xxx; 1}); // 为None返回0,否则执行函数处理Some内部值
option.map_or_else(f1, f2); // 都是闭包,为None和Some分别执行不同函数
and_then
Option内部如果是None 则直接返回None,否则执行传入的闭包,闭包会返回一个Option
此外还有 or() or_else() get_or_insert() get_or_insert_with()
Result
pub enum Result<T, E> {
Ok(T),
Err(E),
}
例如解析字符串失败时返回Result,同时 main 函数也可以返回 Result
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let number_str = "10";
let number = match number_str.parse::<i32>() {
Ok(number) => number,
Err(e) => return Err(e),
};
println!("{}", number);
Ok(())
}
和 Option 类似,也可使用 match 处理。也有 map or and 等简化方法。也有和Option一样的 ?
操作。
可使用 type 关键字起别名
type AliasedResult<T> = Result<T, ParseIntError>;
常用技巧有 Result 和 Option 的相互嵌套,使用Box包装Result