从今天起,开始打算写一些博客记录自己在rust学习的过程中的心得。

NOTE1 rust中的as_ref与容器的结合

这个问题来自于rustlings的algorithm的第四题。
考虑这样的一个数据结构

1
2
3
4
5
6
7
8
struct TreeNode<T>
where
T: Ord,
{
value: T,
left: Option<Box<TreeNode<T>>>,
right: Option<Box<TreeNode<T>>>,
}

现在已经获取了&TreeNode<T>,应该如何获取左右子结点上的TreeNode借用?

由于self&TreeNode<T>,所以self.left就是Option<Box<TreeNode<T>>>。注意,当访问借用来的结构体的字段时,并不会得到字段的借用类型,这一点和C语言是一样的,但是由于是访问的借用来的结构体的字段,所以并不能移动这些字段的所有权。所以,这里不可以直接用unwrap()Option<T>拆开。

那应该怎么写?unwrap()会将其直接拆开,导致所有权move。正确的写法是使用as_ref()(如果是要求mut,就要使用as_mut())把Option<T>转变成Option<&T>,在用unwrap(),这样就只是借用了。

接下来我们得到&Box<TreeNode<T>>,实际上也可以类似的直接用as_ref()得到&TreeNode<T>。这时,&Box<TreeNode<T>>会先自动解引用,再调用as_ref()

因此,这个问题的最终答案是:

1
self.left.as_ref().unwrap().as_ref()

又或者,可以手动执行第二次as_ref的操作(两次解引用:一次auto-deref解&Box,一次解Box本身,然后再来一个引用)得到:

1
&**(root_node.right.as_ref().unwrap())

这个写法更加清晰。

今天就写到这里,事实上虽然同样是调用as_ref()&Option<T>&Box<T>似乎有一些区别,主要在于后者会有一次自动解引用再加&,而前者会直接匹配&self。似乎是由值类型和智能指针之间的区别引起的。不过在最终结果看来实际上二者是相同的,等之后读一下源代码再看看具体的实现是不是这样吧。