我试图实现ConsList个数据 struct ,它来自于函数范式语言,如Lisp或Haskell.下面是它的一些片段:

use ConsList::*;
enum ConsList<T> {
    Empty,
    List(T, Box<ConsList<T>>),
}

impl<T> ConsList<T> {
    fn new() -> ConsList<T> {
        Empty
    }

    // I don't know whether to use & or not at self parameter
    fn insert_first(&mut self, value: T) -> &Self {
        // unkown implementation
    }
    
    fn head(&mut self) -> Option<T> {
        // unkown implementation
    }
}

fn main() {
    let mut list_var: ConsList<i32> = ConsList::new(); // Initiate
    list_var.insert_first(2); // First insert
    list_var.insert_first(1); // Second insert
}

我希望insert_first函数的行为是这样的:

  1. 在新列表元组中的第一个位置创建value的新列表
  2. 将值移动到新列表的尾部
  3. 用新列表替换所有者变量(list_var)

例如,在启动时,list_var变量的值为Empty.然后,在第一次插入时,它将创建新值List(2, Empty),其中2value移动,Empty从Mutations 前的旧列表移动,我们将self变量Mutations 为新列表.换句话说,我们Mutations 了list_var个变量.关于更深入的内容,这里是我在第二次插入时所指的逐步:

  1. list_var(List(2, Empty))移至insert_first方法中的参数self
  2. 1移至insert_first方法中的参数value
  3. 创建新列表,并将valueself移至新列表List(3, List(2, Empty))
  4. 用新列表改变self,所以list_var变量也将是新列表

这里的问题是,我们只能 Select :

  • 在没有引用的情况下转移所有权(我们不能变异list_var)
  • 借入而不能移动(我们不能将旧列表移动到新列表)

据我所知,我们不能在转移所有权的同时又能够随着参照而变异.这是有用的,这是有原因的.方法head将返回列表的第一个元素,并将列表变为尾部(我知道在函数式编程中不存在变异,但这将节省存储空间和复制新列表的性能).此方法需要能够对self进行变异,将第一个元素移动到返回值,并将列表尾部的所有权移动到self.

我试着用方框来表示间接性,但我仍然无法移动self.list,因为它在引用后面

enum ConsListElem<T> {
    Empty,
    List(T, Box<ConsListElem<T>>),
}

pub struct ConstList<T> {
    list: Box<ConsListElem<T>>,
}

impl<T> ConstList<T> {
    pub fn cons(&mut self, value: T) -> &ConstList<T> {
        self.list = Box::new(ConsListElem::List(value, self.list));
        self
    }
}

推荐答案

您可以使用std::mem::replace修改可变引用后面的内容,并返回旧值.因此,在insert_first中,您可以首先用Empty变体替换self,然后创建新的List并将其分配给self.

enum ConsList<T> {
    Empty,
    List(T, Box<ConsList<T>>),
}

impl<T> ConsList<T> {
    fn new() -> ConsList<T> {
        Self::Empty
    }

    fn insert_first(&mut self, value: T) -> &mut Self {
        match self {
            Self::Empty => {
                *self = Self::List(value, Box::new(Self::new()))
            }
            Self::List(_, _) => {
                let old = std::mem::replace(self, Self::new());
                *self = Self::List(value, Box::new(old));
            }
        }
        self
    }
    
    fn head(&self) -> Option<&T> {
        match self {
            Self::Empty => None,
            Self::List(head, _) => Some(head),
        }
    }
}

fn main() {
    let mut list_var: ConsList<i32> = ConsList::new(); // Initiate
    assert_eq!(list_var.head(), None);
    list_var.insert_first(2); // First insert
    assert_eq!(list_var.head(), Some(&2));
    list_var.insert_first(1); // Second insert
    assert_eq!(list_var.head(), Some(&1));
}

Rust相关问答推荐

交叉术语未正确清除屏幕

在特征中使用Async时,如何解决不透明类型`impl Future<;out=self>;`不满足其关联的类型边界和警告?

当rust中不存在文件或目录时,std::FS::File::Create().unwire()会抛出错误

有没有办法指定只在Rust的测试中有效的断言?

Rust&;Tokio:如何处理更多的信号,而不仅仅是SIGINT,即SIGQUE?

在铁 rust 中,如何一次只引用几件事中的一件?

Rust中WPARAM和VIRTUAL_KEY的比较

我应该如何表达具有生命周期参数的类型的总排序,同时允许与不同生命周期进行比较?

为什么将易错函数的泛型结果作为泛型参数传递 infer ()?不应该是暧昧的吗?

.在 Rust 模块标识符中

使用占位符获取用户输入

由特征键控的不同 struct 的集合

不安全块不返回预期值

了解 Rust 闭包:为什么它们持续持有可变引用?

当你删除一个存在于堆栈中的值时,为什么 rust 不会抱怨

&str 的编译时拆分是否可能?

预期类型参数,发现不透明类型

提取 struct 生成宏中字段出现的索引

如何断言代码不会在测试中编译?

当引用不再被borrow 时,Rust 不会得到它