这里之所以能有两个相同名称的赋值.引用官方解释.说 const S是在值命名空间,而S = S{ x: 2}的 S 在类型命名空间.所以能同时出现
One additional wrinkle in this code is the concept of namespaces and name resolution in Rust. Any name that refers to a type lives in the type namespace, and any name that refers to a value lives in the value namespace. These are two separate sets of names, and the language is structured such that we can always tell which namespace to look up a name in.
In the context of the quiz code, the name of the struct S is part of the type namespace and the name of the const S is part of the value namespace. That is how we can have seemingly two different things with the same name in scope at the same time.
但是有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);
}
let [x, y] = &mut[S, S];let 匹配模式x = &mut S, y = &mut S这里两个 S当然是不同的实例,而此时两个地址相同 的!
优先级 as == =依次下降,引用&mut x转换成原生指针x as *mut Sbool 类型和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.
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();
}
总有种看谭浩强的感觉 = =
大量参考
《Rust Quiz 解读》汇总
Rust Quiz
Quiz 2
Rust Quiz 2
结构体元组只有一个元素时候可以组成 New Type
Trait 重载 关联类型
语句和表达式的区别.
{}
默认返回()
空元组{}
也称作块,顺序将多条表达式进行解析fn bitand(self, rhs: S) {}
==fn bitand(self, rhs: S) -> () {}
Quiz 3
这系列尼玛是rust儿童鞋垫
我们来挨个看看
具名结构体
struct S
中的 S是定义在了类型命令空间const 定义的是常量,而 rust 中的 const相当于 C/C++的define,只是在编译器将用到的地方给替换了
这写法等于
const S: S{ x: 2};
,中间的S = S{ x: 2};
只是类型声明表示是 S 结构体类型,如果用过rust-analyzer 的话,就会发现都是这么标记的.这里之所以能有两个相同名称的赋值.引用官方解释.说
const S
是在值命名空间,而S = S{ x: 2}
的 S 在类型命名空间.所以能同时出现这里的 S 以后显然不是类型,而是值.
之前说过相当于直接替换.这里输出
32
.v.x
是被绑定了的,而两个S.x
在栈上是两个不同的临时结构体,默认 x为2,S { x: 2 }.x += 1;
在离开分号就被析构了,和print
完全无关S { x: 2 }.x
这种写法相当于在栈上创建了一个临时结构体访问其 x 成员,在出分号以后就析构了.再考虑以下,最后输出
3 3
,没有警告.这里申明的是const
常量结构体,而且结构体里成员是默认不能被修改的.但是如果是这种临时变量统统可以不管了,甚至还可以输出和使用
但是离开分号就析构,从安全角度来说是很难利用的= =
可以想想是否可以通过引用或者配合其他的缺陷来搞搞
Quiz 4
代码很简短
涉及到模式匹配,在匹配模式中
..
代表要匹配任意项目, 而右边的..
代表全范围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个以上是将utf-8数组转换成字符串
最后输出还是默认十进制
Quiz 5
Rust 中的高阶生命期约束
Higher-Rank Trait Bounds (HRTBs)
草 越来越烦了
这个让我回去又把生命周期重新回顾了一遍
第一个和第三个没问题, 由
_
的形态决定推导的类型.重点是第二个 , 这里涉及到所谓的
HRTBs
,你 Trait 的泛型的生命周期是需要在执行fn p()
才能决定,那什么时候来决定泛型<T>
的生命周期呢所以对于
fn(&T)
,(&'x u8
中的'x
此时不是表示引用,而是等到决定的具体数值.Quiz 6
这个简单点了= =,不过还是很阴
let a = a = true
可以写成let a = (a = true)
等于let a = ()
,(a = true)
这是个赋值表达式,返回一个单位元组. 为什么(a = true)
这里的a
被推断成True
类型,为什么a = ()
能成立呢这里涉及到变量屏蔽
a = true
的a
是第四行定义的,而此时 let a
第五行的a
对上一行的重新绑定不过自己理解肯定还有很大的问题 = =
Quiz 7
简单点了,
Enum
的Frist
和Second
像 C 一样在内存中表示为 0 和 1.所以直接用数字去匹配也可以重点是这里的
match self
中 没有加Enum::
前缀,导致unreachable pattern
模式,Frist 和 Second 都解释成通配符_
这里为啥不直接报错呢= =.
transmute
这个函数有点危险,有潜力Quiz 10
理解 Rust 2018 edition 的两个新关键字 —— impl 和 dyn
捋捋 Rust 中的 impl Trait 和 dyn Trait
这系列尼玛是rust儿童鞋垫
Quiz 12
结构体匹配,第一次听说这玩意.之前遇到元组匹配还很好理解.结构体匹配一时间没转过弯来,想着这结构体里的变量怎么单独拿出来用了= =
先析构没有被绑定的结构体实例,再析构没有被绑定的
D(1)
但是有
ref
的就不一样了,他ref x
等于let x = &S{x: 4}.x
所以结构体实例被绑定了的,里面的D(3)
和结构体不能先析构.只有等 x 打印了,这个结构体没用了再析构人话👇
Quzi 13
怎么这一天都在关心内存该怎么分配, 语法特性细节编译器是怎么绕过你想象力去实现的.你们害人不浅啊 (
看 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 互换应该不用解释了Quiz 14
为 Trait 限制了 Sized 限制.Trait 不能当做对象来使用是什么意思?
match 0.is_reference()
自动引用(&0).is_reference
?impl
放在哪是没关系的,impl 对全局可见还有自动引用
What are Rust's exact auto-dereferencing rules?
Quiz 15
这个 Quiz 5 讲过
Quiz 16
草 C 语言学魔怔了
Quiz 17
C/C++ 转 rust 迷惑鉴赏
Quiz 18
这里到底是将
print2
作为指针传入结构体f: fn()
再去调用(S {f: print2}.f)()
还是直接调用结构体 S 的方法fn 呢
结果是后者
Quiz 19
_
类型推导的占位符不占有所有权,但是却会绑定住不让析构掉Quiz 20
break value in loop
break 也能返回值 那么就要执行后面的表达式= =