Rust Quizes解答

关于Rust语法的测试题,题目来源Rust Quiz

What is the output of this Rust program?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
macro_rules! m {
($($s:stmt)*) => {
$(
{ stringify!($s); 1 }
)<<*
};
}

fn main() {
print!(
"{}{}{}",
m! { return || true },
m! { (return) || true },
m! { {return} || true },
);
}

答案:112

m宏匹配唯一一个分支模式($($s:stmt)*),它表示匹配任意数量的statementstatement在Rust中可以解释为:

  • 变量,如x
  • 语句,如let x = 5;
  • 表达式,如{ 5 }

宏展开时,使用<<作为连接符,由于表达式末尾的1,宏展开时每个匹配的值为固定的1。换而言之,宏的值为匹配的数量减1的2次幂。

在题目中展示的三个宏调用中,第一个宏调用中的return || true表示返回一个闭包,故其只有一个statement,值为1。

第二个宏调用中,(return)为一个元组,是一个值,(return) || true是一个表达式,故只有一个statement,值为1。

第三个宏调用中,{ return } || true被解析为两个表达式:一个单独的块表达式{return}和闭包|| true。在宏展开后,它的表达式如下:

1
{ stringfy!("{ return }"; 1)} << { stringfy!("|| true"); 1}

也就是

1
1 << 1

综上,本题的解答为112

What is the output of this Rust program?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct S(i32);

impl std::ops::BitAnd<S> for () {
type Output = ();

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

fn main() {
let f = || ( () & S(1) );
let g = || { () & S(2) };
let h = || ( {} & S(3) );
let i = || { {} & S(4) };
f();
g();
h();
i();
}

答案:123

f(() & S(1))将运算后的值作为闭包的结果,所以它会计算()&S(1)g同理。h中,空表达式默认返回值为(),最外层的()使得空表达式仍与S(3)进行计算。

但是,在i中,{}本身并不会直接参与到计算中,类似于

1
2
3
4
5
{
let x = 4;
x
}
let y = 4;

表达式的值会被直接抛弃,所以i中的&运算符表示引用,没有发生计算。

综上,本题的解答为123

What is the output of this Rust program?

1
2
3
4
5
6
7
8
9
10
11
12
struct S {
x: i32,
}

const S: S = S { x: 2 };

fn main() {
let v = &mut S;
v.x += 1;
S.x += 1;
print!("{}{}", v.x, S.x);
}

答案:32

在rust中的const常量类似于C中的#define,它并不保存一个值,而是在被调用时做表达式替换。

题目中的代码实际上等价于:

1
2
3
4
5
6
7
8
9
10
struct S {
x: i32,
}

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

What is the output of this Rust program?

1
2
3
4
fn main() {
let (.., x, y) = (0, 1, ..);
print!("{}", b"066"[y][x]);
}

答案是:54

在rust中,..有两个含义:

  1. 在表达式中,..表示RangeFull类型的变量,例如v[..]表示v的一个完整切片。
  2. 在模式匹配中,..表示任意数量的元素。

在本题中,(.., x, y)可以匹配任意元素数量大于等于2的元组,并将其最后两个元素赋值给xy,因此x值为1y值为..

输出表达式为b"066"[y][x],也就是b"066"[..][1],而b"066"[u8; 3],我们会先获取一个完整的切片,再输出第二个元素,也就是b'6',其ASCII码为54

What is the output of this Rust program?

1
2
3
4
5
6
7
use std::mem;

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

答案:0

在第二个let语句中事实上发生了变量覆盖,可以看作let a = { a = true };,其为变量a赋值了一个表达式,这个表达式的值是一个语句,也就是说实际值为()()不占用任何空间,所以结果是0。

What is the output of this Rust program?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#[repr(u8)]
enum Enum {
First,
Second,
}

impl Enum {
fn p(self) {
match self {
First => print!("1"),
Second => print!("2"),
}
}
}

fn main() {
Enum::p(unsafe {
std::mem::transmute(1u8)
});
}

答案:1

在这里的match匹配中,First表示匹配成功后的变量名,而不是枚举值,所以总是匹配到First分支。

What is the output of this Rust program?

1
2
3
4
5
6
7
8
9
10
11
12
13
macro_rules! m {
(==>) => { print!("1"); };
(= = >) => { print!("2"); };
(== >) => { print!("3"); };
(= =>) => { print!("4"); };
}

fn main() {
m!(==>);
m!(= = >);
m!(== >);
m!(= =>);
}

答案:1214

在宏的模式匹配中,符号串会从左到右被解析为合法符号的集合。在这四个案例中,四个符号分别解析为:

  1. 等于,大于
  2. 赋值,赋值,大于
  3. 等于,大于
  4. 等于,胖箭头

可见3与1的含义一致,会优先匹配到1,因此输出1214

What is the output of this Rust program?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
macro_rules! m {
(1) => { print!("1") };
($tt:tt) => { print!("2") };
}

macro_rules! e {
($e:expr) => { m!($e) };
}

macro_rules! t {
($tt:tt) => { e!($tt); m!($tt); };
}

fn main() {
t!(1);
}

答案:21

显然,t!(1)会展开为e!(1);m!(1);

其中,e!(1)可以匹配第一个分支。然而,虽然事实上$e1,但是它不能匹配第一个m的分支,因为在展开时,对于m来说,$e就是一个表达式,不能匹配第一个分支,所以输出2。但是m!($tt)可以匹配,因为$tt描述符是一个特殊的描述符。

在rust宏中,有三类描述符可以在匹配后继续匹配值:

  • $:ident
  • $:lifetime
  • $:tt

What is the output of this Rust program?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
trait Trait {
fn f(&self);
}

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

impl Trait for bool {
fn f(&self) {
print!("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);
<dyn Trait as Trait>::f(&true as &dyn Trait);
}