Justin Lei

Justin Lei

Study forever

07 Sep 2021

CS110L Week 3


Part 1

使用 Rust 实现一个小工具 inspect-fds,查看进程打开的文件描述符信息。

找到一个进程打开的所有文件描述符

一个进程打开的文件描述符存放在 /proc/$pid/fd 文件夹下。

因此使用 std::fs::read_dir 遍历当前路径,由于要求 list_fds 函数返回 Option<Vec<usize»,但是 std::fs::read_dir 返回的是 Result,因此我们可以使用 Result.ok()? 将 Result 转换为 Option,当 Result 返回 Err 的时候,会转换为 None

for entry in fs::read_dir(fd_path).ok()? {
     let entry = entry.ok()?;
     let fd = entry.file_name()
                .into_string().ok()?
                .parse::<usize>().ok()?;
     open_fds.push(fd);
}

注意在使用 vscode remote 远程连接时,每个进程会多出 19 和 99 两个文件描述符,查看它们指向的描述符分别是 /dev/ptmx 和 vscode remote 创建的文件。

/dev/ptmx 是 Linux 的伪终端,当打开时,会自动创建 master 和 slave(slave 位于 /dev/pts/ 下),为了实现虚拟化的目标,伪终端模拟器通过master发送键盘输入,bash 等终端进程通过 slave 接收,然后将执行结果通过 slave 发送,伪终端模拟器通过 master 接收处理结果,显示到显示器上。

读取文件描述符信息

由于 /proc/$pid/fd 下所有的文件都是软链接,因此需要使用 fs::read_link 获取文件描述符指向的真实的文件,然后使用 fs::read_to_string 读取 /proc/$pid/fdinfo/$fd 下的信息,然后使用正则表达式提取信息即可得到文件描述符的 AccessMode、Cursor。

使用正则表达式

let re = Regex::new(r"pos:\s*(\d+)").unwrap();
Some(
    re.captures(fdinfo)?
        .get(1)?
        .as_str()
        .parse::<usize>()
        .ok()?,
    )

Part 2

实现一个通用的链表,练习泛型和 trait 的用法

Box<T> 表示在堆上为对象分配内存,返回指向内存的指针

实现泛形

impl<T> LinkedList<T> {
    head: Option<Box<Node<T>>>,
    size: usize,
}

实现 Clone trait

impl<T: Clone> Clone for Node<T> {
    fn clone(&self) -> Self {
        // next 会自动克隆下一个节点
        Node { value: self.value.clone(), next: self.next.clone() }
    }
}

实现 PartialEq trait

impl<T: PartialEq> PartialEq for LinkedList<T> {
    fn eq(&self, rhs: &LinkedList<T>) -> bool {
        if self.size != rhs.size {
            return false;
        } 
        let mut l: &Option<Box<Node<T>>> = &self.head;
        let mut r: &Option<Box<Node<T>>> = &rhs.head;
        loop {
            match l {
                Some(node) => {
                    // node 类型是 &Box<Node<T>>, r 类型是&Option<Box<Node<T>>,如果直接 unwrap,得到的就是 Box<Node<T>>,不能做比较,因此我们需要使用 as_ref,转换成 Option<&Box<Node<T>>,然后再 unwrap
                    let rnode = r.as_ref().unwrap();
                    if *node != *rnode {
                        return false
                    }
                    l = &node.next;
                    r = &rnode.next;
                },
                None => break,
            }
        }
        true
    }
}

注意比较时候的类型转换。

Option::AsRef 和通常的 as_ref 不同,会将 &Option<T> 转换成 Option<&T>,也就是说刚开始对 Option 是没有所有权的,通过 as_ref 转换成了 Option<&T>,就有了所有权,可以调用 unwrap 获取里面的 T 的引用。

实现 Iterator/IntoIterator trait

引用自 这篇 文章,在 Rust 中,为了实现遍历所有元素,通常使用迭代器访问元素,迭代器起到的作用是获取当前值、向前指向下一个值、如果全部已经遍历完就返回 None

如果一个类型实现了 Iterator trait,就可以转换成一个迭代器,调用 next 方法获得集合中的值

而通常使用的 for loop 其实是 IntoIterator trait 的语法糖,其中的 into_iter 方法会返回一个迭代器

trait Iterator {
  type Item
}
trait IntoIterator {
  type Item
  type IntoIter
  fn into_iter(self) -> Iterator; // 可以看到默认的迭代器会将原本的集合的所有权消耗掉,转变成一个迭代器,因此 for loop 使用过后集合就不能再此被使用
}

impl IntoIterator for &Vec<T> {
  type Item
  type IntoIter
  fn into_iter(self) -> Itearator<Item=&T> {
    // 转换成引用迭代器
  }
}

对于我们的 LinkedList 来说,为了实现

for item in &list {
  ...
}

我们需要为 &LinkedList<T> 实现 IntoIterator,将其转换成 LinkedListIter,LinkedListIter 需要实现 Iterator trait,对链表里的 value 进行克隆

pub struct LinkedListIter<'a, T> {
    current: &'a Option<Box<Node<T>>>
}

impl<T: Clone> Iterator for LinkedListIter<'_, T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        match self.current {
            Some(node) => {
              	// 此处直接使用拷贝
                Some(node.value.clone())
            },
            None => None
        }
    }
}

// 引用情况下
impl<'a, T: Clone> IntoIterator for &'a LinkedList<T> {

    type Item = T;
    type IntoIter = LinkedListIter<'a, T>;

    fn into_iter(self) -> LinkedListIter<'a, T> {
        LinkedListIter { current: &self.head }
    }
}

如果不使用拷贝形式,而是用引用形式,则可以按照下面方法实现

impl<'a, T: Clone> IntoIterator for &'a LinkedList<T> {

    type Item = &'a T; // 此处变为引用
    type IntoIter = LinkedListIter<'a, T>;

    fn into_iter(self) -> LinkedListIter<'a, T> {
        LinkedListIter { current: &self.head }
    }
}

impl<'a, T: Clone> Iterator for LinkedListIter<'a, T> {
    type Item = &'a T; // 此处变为引用
    fn next(&mut self) -> Option<&'a T> { // Option 返回的类型为引用值
        match self.current {
            Some(node) => {
                self.current = &node.next;
                Some(&node.value) // 返回引用
            },
            None => None
        }
    }
}

如果需要实现

for item in list {
  ...
}

我们可以直接为 LinkedList<T> 实现 Iterator

impl <T: Clone> Iterator for LinkedList<T> {
  type Item
  fn next(&mut self) -> Option<T>
}