zoniony / zoniony.github.io

blog
1 stars 0 forks source link

Quiz Part #10

Open zoniony opened 4 years ago

zoniony commented 4 years ago

总有种看谭浩强的感觉 = =

大量参考

《Rust Quiz 解读》汇总

Rust Quiz

Quiz 2

Rust Quiz 2

struct S(i32);

impl std::ops::BitAnd<S> for () {

    type Output = ();

    fn bitand(self, rhs: S) {
        println!("{}", rhs.0);
    }

}

fn main() {

    let f = ||( () & S(1));
    let g = ||{ () & S(2)};
    let h = ||( {} & S(3));
    let u = ||{ {} & S(4)};
    f();
    g();
    h();
    u();
}
/*
Output:
1
2
3
*/

Quiz 3

这系列尼玛是rust儿童鞋垫

我们来挨个看看

struct S {
    x: i32,
}

const S: S = S{ x: 2};
fn main() {
    let v = &mut S;
    v.x += 1;
    S.x += 1;
    println!("v.x->{}",v.x);
    println!("S.x->{}",S.x);
}

具名结构体struct S中的 S是定义在了类型命令空间

struct S {
    x: i32,
}
const S: S = S{ x: 2};

这里的 S 以后显然不是类型,而是值.

    let v = &mut S;
    v.x += 1;
    S.x += 1;

之前说过相当于直接替换.这里输出32.v.x是被绑定了的,而两个 S.x在栈上是两个不同的临时结构体,默认 x为2,S { x: 2 }.x += 1;在离开分号就被析构了,和print完全无关

let mut _tmp0 = S { x: 2 };
let v = &mut _tmp0;
S { x: 2 }.x += 1;
print!("{}{}", v.x, S.x);

S { x: 2 }.x这种写法相当于在栈上创建了一个临时结构体访问其 x 成员,在出分号以后就析构了.

再考虑以下,最后输出 3 3,没有警告.这里申明的是const 常量结构体,而且结构体里成员是默认不能被修改的.

但是如果是这种临时变量统统可以不管了,甚至还可以输出和使用

struct S {
    x: i32,
}

const S: S = S{ x: 2};
fn main() {
    let v = &mut S;
    v.x += 1;
    S.x += 1;
    S.x;
    S.x;
    S.x;
    S.x;
    println!("v.x->{}",v.x);
    println!("S.x->{}",S.x+1);
}

但是离开分号就析构,从安全角度来说是很难利用的= =

可以想想是否可以通过引用或者配合其他的缺陷来搞搞

Quiz 4

代码很简短

fn main() {
    let (.., x, y) = (0, 1, ..);
    println!("{}", b"066"[y][x]);
    println!("{}",[48u8, 54u8, 54u8][..][1]);
}

涉及到模式匹配,在匹配模式中..代表要匹配任意项目, 而右边的..代表全范围std::ops::RangeFull全范围

b"066" 是语法糖, 这里访问字符串和访问ascii一样

接着浪费我最久时间的时间是理解像[1, 2, 3, 4, 5][..][0]这种访问方式

在 C/C++ 看惯了,一时间没从多维数组中逃脱出来

[1, 2, 3, 4, 5][..]先将数组转换成有 5 个元素的[u8]切片

[1, 2, 3, 4, 5][..][x]代表访问第x个

fn main() {
    let d = [48u8, 54u8, 54u8];
    println!("{:?}",std::str::from_utf8(&d).unwrap());
}

以上是将utf-8数组转换成字符串

最后输出还是默认十进制

Quiz 5

Rust 中的高阶生命期约束

Higher-Rank Trait Bounds (HRTBs)

草 越来越烦了

trait Trait {
    fn p(self);
}

impl<T> Trait for fn(T) {
    fn p(self) {
        print!("1");
    }
}

impl<T> Trait for fn(&T) {
    fn p(self) {
        print!("2");
    }
}

fn f(_: u8) {}
fn g(_: &u8) {}

fn main() {
    let a: fn(_) = f;
    let b: fn(_) = g;
    let c: fn(&_) = g;
    a.p();
    b.p();
    c.p();
}

这个让我回去又把生命周期重新回顾了一遍

第一个和第三个没问题, 由_的形态决定推导的类型.

重点是第二个 , 这里涉及到所谓的 HRTBs,你 Trait 的泛型的生命周期是需要在执行fn p()才能决定,那什么时候来决定泛型<T>的生命周期呢

所以对于fn(&T),(&'x u8中的'x此时不是表示引用,而是等到决定的具体数值.

Quiz 6

use std::mem;

fn main() {
    let a;
    let a = a =true;
    println!("{}",mem::size_of_val(&a));   
}

这个简单点了= =,不过还是很阴

let a = a = true 可以写成 let a = (a = true) 等于 let a = (),(a = true)这是个赋值表达式,返回一个单位元组. 为什么(a = true)这里的a被推断成True类型,为什么a = ()能成立呢

这里涉及到变量屏蔽 a = truea是第四行定义的,而此时 let a第五行的a对上一行的重新绑定

不过自己理解肯定还有很大的问题 = =

Quiz 7

#[repr(u8)]
enum Enum {
    Frist,
    Second,
}

impl Enum {
    fn p(self) {
        match self {
            Frist => println!("1"),
            Second => println!("2"),
        }
    }
}
fn main() {
    Enum::p(unsafe{
        std::mem::transmute(1u8)
    });
}

简单点了,EnumFristSecond像 C 一样在内存中表示为 0 和 1.所以直接用数字去匹配也可以

重点是这里的match self中 没有加Enum::前缀,导致unreachable pattern模式,Frist 和 Second 都解释成通配符_

这里为啥不直接报错呢= =.

transmute这个函数有点危险,有潜力

Quiz 10

理解 Rust 2018 edition 的两个新关键字 —— impl 和 dyn

捋捋 Rust 中的 impl Trait 和 dyn Trait

trait Trait {
    fn f(&self);
}

impl<'a> dyn Trait + 'a {
    fn f(&self) {
        println!("1");
    }
}

impl Trait for bool {
    fn f(&self) {
        println!("2");
    }
}

fn main() {
    Trait::f(&true);
    Trait::f(&true as &dyn Trait);
    <_ as Trait>::f(&true);
    <_ as Trait>::f(&true as &dyn Trait);
    <bool as Trait>::f(&true);
}

目前,Rust还不提供直接调用trait对象中定义的默认实现的语法

这系列尼玛是rust儿童鞋垫

Quiz 12

struct D(u8);

impl Drop for D {
    fn drop(&mut self) {
        println!("{}", self.0);
    }
}

struct S {
    d: D,
    x: u8,
}
fn main() {
    let S {x, ..} = S {
        d: D(1),
        x: 2,
    };

    println!("{}", x);

    let S {ref x, ..} = S {
        d: D(3),
        x: 4,
    };
    println!("{}", x);
}

​ 结构体匹配,第一次听说这玩意.之前遇到元组匹配还很好理解.结构体匹配一时间没转过弯来,想着这结构体里的变量怎么单独拿出来用了= =

​ 先析构没有被绑定的结构体实例,再析构没有被绑定的D(1)

但是有ref的就不一样了,他ref x 等于 let x = &S{x: 4}.x所以结构体实例被绑定了的,里面的D(3)和结构体不能先析构.只有等 x 打印了,这个结构体没用了再析构

人话👇

In the second let-binding, we borrow a field x from the owner of a value of type S. The whole value of type S remains in scope during the time that its field x is borrowed, and goes out of scope at the close curly brace of main.

Quzi 13

struct S;

fn main() {
    let [x, y] = &mut[S, S];
    let eq = x as *mut S == y as *mut S;
    println!("{}", eq as u8);
}

怎么这一天都在关心内存该怎么分配, 语法特性细节编译器是怎么绕过你想象力去实现的.你们害人不浅啊 (

看 Rust深入浅出的时候,知道在 Debug 和 Release模式下,分配单元结构体或者元组的时候,分配的地址是不一样的.

这里就想出了一个骚套路让你明白分配内存地址的细节

let [x, y] = &mut[S, S];let 匹配模式x = &mut S, y = &mut S这里两个 S当然是不同的实例,而此时两个地址相同 的!

优先级 as == =依次下降,引用&mut x转换成原生指针x as *mut S bool 类型和0 1 互换应该不用解释了

Ordinarily having multiple mutable references to the same memory location would not be safe, but in the case of mutable references to zero sized types, dereferencing is a no-op so there is no way to violate any memory safety guarantees this way.

Quiz 14

trait Trait: Sized {
    fn is_reference(self) -> bool;
}

impl <'a, T> Trait for &'a T {
    fn is_reference(self) -> bool {
        true
    }
}
fn main() {
    match 0.is_reference() {
        true => println!("1"),
        false => println!("0"),
    }

    match '?'.is_reference() {
        true => println!("1"),
        false => {
`
            println!("0");
        }
    }
}

为 Trait 限制了 Sized 限制.Trait 不能当做对象来使用是什么意思?

match 0.is_reference()自动引用(&0).is_reference?

impl放在哪是没关系的,impl 对全局可见

            impl Trait for char {
                fn is_reference(self) -> bool {
                    false
                }
            }

In particular, that impl is visible throughout the whole program, not just within the block containing the impl.

还有自动引用

What are Rust's exact auto-dereferencing rules?

Quiz 15

trait Trait {
    fn f(&self);
}

impl Trait for u32 {
    fn f(&self) {
        println!("1");
    }
}

impl<'a> Trait for &'a i32 {
    fn f(&self) {
        println!("2");
    }
}

fn main() {
    let x = &0;
    x.f();
}

这个 Quiz 5 讲过

If we want to resolve the trait method call Trait::f(x), we find that its argument x must be of type &Self for some type Self that implements Trait. We find that inferring 0: u32 satisfies both the constraint that u32 is an integer as well as u32 implements Trait, so the method call ends up calling ::f(x) and prints 1

Quiz 16

fn main() {
    let a = 2;
    println!("{} {}",--a ,--a);
}

草 C 语言学魔怔了

Why doesn't Rust have increment and decrement operators? Preincrement and postincrement (and the decrement equivalents), while convenient, are also fairly complex. They require knowledge of evaluation order, and often lead to subtle bugs and undefined behavior in C and C++. x = x + 1 or x += 1 is only slightly longer, but unambiguous.

In the absense of a decrement operator, --x is parsed as -(-x). In the case of x = 4 this would be -(-4) which is 4.

Quiz 17

fn main() {
    let a = 3;
    let b = 2;
    let c = a-- - --b;
    let d= a - (----b);
    println!("{} {}", c, d);
}

C/C++ 转 rust 迷惑鉴赏

Quiz 18

struct S {
    f: fn(),
}

impl S {
    fn f(&self) {
        println!("1");
    }
}

fn main() {
    let print2 = || println!("2");
    S {f: print2}.f();
}

这里到底是将 print2作为指针传入结构体 f: fn()再去调用(S {f: print2}.f)()

还是直接调用结构体 S 的方法fn 呢

结果是后者

Quiz 19

struct S;

impl Drop for S {
    fn drop(&mut self) {
        println!("1");
    }
}

fn main() {
    let s =S ;
    let _ = s;
    print!("2"); 
}

_类型推导的占位符不占有所有权,但是却会绑定住不让析构掉

Quiz 20

fn return1() {
    if (return { print!("1") }) {
    }
}

fn return2() {
    if return { print!("2") } {
    }
}

fn break1() {
    loop {
        if (break { print!("1") }) {
        }
    }
}

fn break2() {
    loop {
        if break { print!("2") } {
        }
    }
}

fn main() {
    return1();
    return2();
    break1();
    break2();
}

break value in loop

break 也能返回值 那么就要执行后面的表达式= =