创建一个简单的链表

时间:2015-07-15 06:41:10

标签: rust

我很难让借阅检查程序为一个简单的迭代链表构建器工作。

C

我尝试编译时收到的错误:

true

我想要的是相当简单的。我们遍历Vec,在每次迭代时创建一个新节点。如果prev为None,则必须是start,因此我们使root变量取得第一个节点的所有权。如果不是,我们会更新上一个节点的下一个值以指向此节点。

我是Rust的新手,所以我不确定自己哪里出错了。我的解释是,借用检查员并没有很好地处理这个问题。它无法推断出匹配中的None分支,包含' root'赋值,只会被调用一次,导致有关root的两个错误被借用两次。我是对的吗?

这种方法在Rust中可行吗?是否有更惯用的方法来做这类事情?

(递归方法可能更容易,但我想完成一个迭代的方法作为学习练习。)

1 个答案:

答案 0 :(得分:6)

首先,您应该确保已阅读并理解OwnershipReferences and Borrowing上的Rust Book章节。你当前的问题是,你正在借用任何东西拥有的东西,因此就会消失。您还有其他问题,例如尝试通过不可变指针进行变异。

让我们得到的东西至少可以起作用:

fn main() {
    let v = vec![1,5,3,8,12,56,1230,2,1];
    let mut root: Option<Box<Node>> = None;

    for i in v.into_iter().rev() {
        root = Some(Box::new(Node { value: i, next: root }));
    }

    println!("root: {}",
        root.map(|n| n.to_string()).unwrap_or(String::from("None")));
}

struct Node {
    value: i32,
    next: Option<Box<Node>>,
}

impl std::fmt::Display for Node {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        let mut cur = Some(self);
        let mut first = true;
        try!(write!(fmt, "["));
        while let Some(node) = cur {
            if !first { try!(write!(fmt, ", ")); }
            first = false;
            try!(write!(fmt, "{}", node.value));
            cur = node.next.as_ref().map(|n| &**n);
        }
        try!(write!(fmt, "]"));
        Ok(())
    }
}

这构造了一个列表并显示了如何迭代显示它。请注意构造代码完全没有借用。

有点作弊,因为我向后迭代了向量来构建列表。

原始代码的问题在于,即使您删除了所有不必要的内容,也可以使用以下内容:

let v = vec![1,5,3,8,12,56,1230,2,1];
let mut v = v.into_iter();

let mut root: Option<Box<Node>> = None;
if let Some(i) = v.next() {
    root = Some(Box::new(Node { value: i, next: None }));
    let mut prev: &mut Box<Node> = root.as_mut().unwrap();

    for i in v {
        let curr = Some(Box::new(Node { value: i, next: None }));
        prev.next = curr;
        prev = prev.next.as_mut().unwrap();
    }
}

你仍然会遇到这样一种情况:编译器看到你改变了你用第二条路径借来的东西。理解重新分配prev并不实际创建任何别名并不够智能。另一方面,如果您将循环分解为等效的递归:

if let Some(i) = v.next() {
    root = Some(Box::new(Node { value: i, next: None }));

    fn step<It>(prev: &mut Box<Node>, mut v: It) where It: Iterator<Item=i32> {
        if let Some(i) = v.next() {
            let curr = Some(Box::new(Node { value: i, next: None }));
            prev.next = curr;
            step(prev.next.as_mut().unwrap(), v)
        }
    }

    step(root.as_mut().unwrap(), v);
}

然后它完全没问题。遗憾的是,即使启用了优化,在这种情况下Rust也不会执行尾调用。因此,在借用检查器限制和缺乏保证尾部调用消除之间,这种设计在安全代码中可能是不可能的。

我自己遇到了这个问题;循环和&mut指针并不总是很好地相互配合。您可以通过切换到RefCell及其相关的运行时成本来解决此问题,尽管这会使在循环中迭代这样的列表变得复杂。另一个替代方法是使用usize而不是指针,并将所有节点分配到某个地方的Vec,尽管这会引入边界检查开销。

如果没有这一切,那就是unsafe代码,它可以让你或多或少地准确写出你用其他语言写的东西,比如C或C ++,但是没有Rust通常的安全保证。

在一天结束时,编写的数据结构只是包装在安全Rust中的现有数据结构而没有开销,这是不可能的。这就是为什么Rust中的基本数据结构都是使用一些不安全的代码编写的。

相关问题