yetone / blog

7 stars 0 forks source link

[WIP] Rust 学习📓 #2

Open yetone opened 7 years ago

yetone commented 7 years ago

解引用强制多态会自动的将指针或智能指针的引用转换为指针内容的引用。

use std::ops::Deref;

struct A(i32);

impl Deref for A {
    type Target = i32;

    fn deref(&self) -> &i32 {
        &self.0
    }
}

fn add_one(v: &i32) -> i32 {
    v + 1
}

fn main() {
    let a = A(1);
    // impl Deref for A {type Target = B;}
    // &A == &B
    assert_eq!(add_one(&a), 2);
}

解引用时 rust 会偷偷地调用 deref 方法:

*a == *(a.deref())

强制解引用多态是针对函数参数的,是 rust 为了方便传递引用类型的参数而提供的语法糖,为了把不匹配的引用类型转换成与函数签名中的参数相同的类型而做的语法糖:

fn hello(name: &str) {
    println!("hello {}", name);
}

// hello(&a) == hello(a.deref())

scan 和 fold 的区别:

scan 是个适配器(返回 Iterator),fold 是个消费器(返回实现了 FromIter 的类型)。scan closure 的第一个参数是个可变引用 &mut,每次迭代需要改变第一个参数,且 closure 的返回值需要是个 Optional

fn main() {
    let a = [1,2,3,4];
    a.iter().scan(0, |a, c| {
        println!("scan: {} {}", a, c);
        *a = *a + c;
        Some(*a)
    }).collect::<Vec<_>>();
    a.iter().fold(0, |a, c| {
        println!("fold: {} {}", a, c);
        a + c
    });
}

输出:

scan: 0 1
scan: 1 2
scan: 3 3
scan: 6 4
fold: 0 1
fold: 1 2
fold: 3 3
fold: 6 4

slice a &str

let s = "abcdefghi" // &str;
s[1..3]; // str
&s[1..3]; // &str
s[1..3].to_string(); // String
let s = String::from("abcdef"); // String
s[1..3]; // str
yetone commented 6 years ago

所有权系统

所有权系统是为了管理上的

记录何处的代码在使用堆上的什么数据,最小化堆上的冗余数据的数量以及清理堆上不再使用的数据以致不至于耗尽空间,这些所有的问题正是所有权系统要处理的。

所有者(owner)

所有者就是变量有且只有一个所有者(变量)

当所有者离开作用域(scope)后,值就会被丢弃(释放内存)。

yetone commented 6 years ago

类型构造器

类型构造器就是一些带有泛型/模板参数的类型。当填满了参数,才会成为一个实际的类型。& 和 &mut 都是类型构造器,它们的参数是生命周期和具体的类型。

yetone commented 6 years ago

Move

let s1 = String::from("hello");
let s2 = s1; // move

获得所有权

trpl04-04

Borrow(Reference)

let s = &s1;

不获得所有权

trpl04-05

let mut a = 1;
let b = &mut a;
             - borrow of `a` occurs here
let c = a + 1;
        ^ use of borrowed `a`
error[E0503]: cannot use `a` because it was mutably borrowed

下面是正常的,嘻嘻:

let mut a = 1;
let b = &a;
let c = a + 1;
let mut a = 1;
let b = &a;
         - borrow of `a` occurs here
a = 2;
^^^^^ assignment to borrowed `a` occurs here
error[E0506]: cannot assign to `a` because it is borrowed
yetone commented 6 years ago

Partially Moved

#![allow(unused_variables)]

#[derive(Debug)]
struct A(i32);

#[derive(Debug)]
struct B {
    a: A,
    aa: A,
}

fn main() {
    let b = B { a: A(1), aa: A(2) };
    let a = b.a;
     // - value moved here
    println!("{:?}", b);
                  // ^ value used here after move
}

To fix it:

#![allow(unused_variables)]

#[derive(Debug, Copy, Clone)] // <- important
struct A(i32);

#[derive(Debug)]
struct B {
    a: A,
    aa: A,
}

fn main() {
    let b = B { a: A(1), aa: A(2) };
    let a = b.a;
    println!("{:?}", b);
}

Alternative

let b = &B { a: A(1), aa: A(2) };
let a = &b.a;
println!("{:?}", b);

If you:

let b = &B { a: A(1), aa: A(2) };
let a = b.a;
//|     ^---
//|     |
//|     cannot move out of borrowed content
//|     help: consider using a reference instead: `&b.a`

println!("{:?}", b);
yetone commented 6 years ago

模式匹配

除了 let, match, if let, while let 以外,函数参数也支持模式匹配哦!!!(我好蠢,我现在才知道 -,-

use std::ops::BitOr;

#[derive(Debug, PartialEq)]
struct BooleanVector(Vec<bool>);

impl BitOr for BooleanVector {
    type Output = Self;

    fn bitor(self, BooleanVector(rhs): Self) -> Self {
                              // ^ rhs: Vec<_>
        let BooleanVector(lhs) = self;
        assert_eq!(lhs.len(), rhs.len());
        BooleanVector(lhs.iter().zip(rhs.iter()).map(|(x, y)| *x || *y).collect())
    }
}

let bv1 = BooleanVector(vec![true, true, false, false]);
let bv2 = BooleanVector(vec![true, false, true, false]);
let expected = BooleanVector(vec![true, true, true, false]);
assert_eq!(bv1 | bv2, expected);
let a = &1;
match a {
  &x if x > 1 => println("a: {}", x),
  _ => println("hehe")
}
let a = &mut 1;
match a {
  &mut x if x > 1 => println("a: {}", x),
  _ => println("hehe")
}
yetone commented 6 years ago

trait

supertrait

看起来像继承,其实不是继承啊,傻子

trait A {}
trait B: A {}

意味着,你要实现 B,必须得先实现 A:

struct S;
impl A for S; // 如果漏掉了这一行就跪了
impl B for S;
yetone commented 6 years ago

never type

! 是 never type,可以强制转换成任意类型,嘻嘻

其实就是 bottom type 而已(官方承认了

yetone commented 6 years ago

Sized

struct Foo<T>(T);
struct Bar<T: ?Sized>(T);

// struct FooUse(Foo<[i32]>); // error: Sized is not implemented for [i32]
struct BarUse(Bar<[i32]>); // OK
trait T {
  fn test(self) {}
          ^^^^ doesn't have a size known at compile-time
}
error[E0277]: the size for values of type `Self` cannot be known at compilation time

下面是好的:

trait T: Sized {
  fn test(self) {}
}

或者:

trait T {
  fn test(self) where Self: Sized {}
}

因为局部变量、函数参数、函数返回值都是放在栈上的,所以要求它们必须大小已知且固定,所以它们必须是 Sized,所以泛型参数默认是 Sized:

fn test<T>(a: T) {}

上面是合法的,因为默认 T: Sized,但是为什么给我们一个 ?Sized 用来去掉 Sized bound 的功能呢?想象下面的情况:

fn test<T>(a: &T) {}

是不是 T 即使可变大小也是合法的?(因为指针是固定大小的),所以上面的函数可以:

fn test<T: ?Sized>(a: &T) {}

详情: https://stackoverflow.com/a/30941589/4568509

yetone commented 6 years ago

生命周期

生命周期就是一个区间,生命周期参数就是一个普通的泛型参数,它可以被特化为某个具体的生命周期。

yetone commented 6 years ago

FAQ

1. 为什么不能为 &mut i32 使用加法?

let a = 1;
let b = a + 1;
let a = &1;
let b = a + 1; // 正常
let a = &mut 1;
let b = a + 1; // 报错
// error[E0369]: binary operation `+` cannot be applied to type `&mut {integer}`

因为 std::ops::Add 只为 i32 实现了两个:

fn add(self, i32) -> i32;
fn add(&self, &i32) -> i32;

你说气人不气人?!

2. 如何打印一个变量的类型?

两种方法,第一种是利用 rust 的类型系统的报错信息:

fn t(_x: ()) {}

let ref a = 1;
t(a);

第二种比较高级:

#![feature(core_intrinsics)]

fn pt<T>(_arg: &T) {
    unsafe {
        println!("{}", std::intrinsics::type_name::<T>());
    }
}

let ref a = 1;
pt(&a);

3. 如何计算一个表达式的执行时间

macro_rules! timeit {
    ($e:expr) => {{
        use std::time;
        let st = time::SystemTime::now();
        let rest = $e;
        match st.elapsed() {
            Ok(elapsed) => {
                println!(
                    "{} cost: {}",
                    stringify!($e),
                    if elapsed.as_secs() > 1 {
                        format!("{}s", elapsed.as_secs())
                    } else if elapsed.as_millis() > 1 {
                        format!("{}ms", elapsed.as_millis())
                    } else if elapsed.as_micros() > 1 {
                        format!("{}μs", elapsed.as_micros())
                    } else {
                        format!("{}ns", elapsed.as_nanos())
                    }
                );
            }
            Err(_) => {}
        }
        rest
    }};
}

let a = timeit!(1 + 1);
yetone commented 6 years ago

dyn trait

dynamic trait 其实就是 &Trait, &mut Trait, Box<Trait>,这么做是为了和 impl Trait 相对应:

&dyn Trait, &mut dyn Trait, Box<dyn Trait>

详见:https://joshleeb.com/posts/rust-traits-and-trait-objects/·

yetone commented 6 years ago

Closure

其实 |x| x + 1 并不是一个 closure,因为并没有捕获局部变量(非常量和静态变量),所以 rust 会把它当作普通函数处理:

let plus_one: fn(i32) -> i32 = |x| x + 1; // 这是合法的
const a: i32 = 1;
let plus_one: fn(i32) -> i32 = |x| x + a; // 这也是合法的
static a: i32 = 1;
let plus_one: fn(i32) -> i32 = |x| x + a; // 这同样是合法的

作为对比,下面的情况会报错:

let a = 1;
let plus_one: fn(i32) -> i32 = |x| x + a; // 这是不合法的,因为 |x| x + a 是个 closure 并不是 function pointer
yetone commented 6 years ago

Macro

最后一点是十分重要的,因为它有重要的意义。因为宏被解析成另 AST,它只能出现在明确支持的位置。 具体来说,宏只能出现在下列位置: