请注意,这个问题指的是Rust 1.0之前的版本.尽管语法发生了变化,但这些概念仍然有效.

使用链接指针可以轻松实现以下功能:

struct Node<T> {
  next: Option<~Node<T>>,
  data: T
}

但是,想象一下,如果您想要高效地实现一个支持四种基本操作的队列:

  • push:添加到列表末尾
  • pop:从列表末尾删除并返回
  • unshift:添加到列表的前面
  • shift:从列表末尾删除并返回

在使用普通指针的语言中,可以使用双向链表和根对象来实现这一点,根对象存储指向列表中第一个和最后一个元素的firstlast个指针.

我看不出你会如何在铁 rust 中实现这一点.

我可以隐约猜到,你会使用一堆参考资料,或许是这样的:

struct Node<T> {
  next: Option<&Node<T>>,
  prev: Option<&Node<T>>,
  data: T
}

...但我看不出你是如何管理这些变量的生命周期范围的.

有人能告诉我这个方向吗?或者是一个类似的例子,涉及到对象之间引用的复杂生命周期?

(Another typical example of this style of code would be the observer pattern where many objects must publish event updates to a single location, eg. UINode <>---- EventObserver <>---- EventCore <>---- UINodes; multiple objects in a complex hierarchy sharing pointers, where events propagate from leaf nodes down to some core and then get pushed out to different leaf nodes)

推荐答案

我建议你看看Lars Bergstrom写的Rust patterns.

以下是实现双链接列表的代码,更新自@Yurume的Rust 1.12(未完全测试)

use std::mem;
use std::ptr;

pub struct List<T> {
    list_head: Option<Box<Node<T>>>,
    list_tail: Rawlink<Node<T>>,
}

struct Rawlink<T> { p: *mut T }

impl<T> Copy for Rawlink<T> {}

impl<T> Clone for Rawlink<T> {
    fn clone(&self) -> Self { Rawlink { p: self.p } }
}

pub struct Node<T> {
    next: Option<Box<Node<T>>>,
    prev: Rawlink<Node<T>>,
    value: T,
}

impl<T> List<T> {
    pub fn is_empty(&self) -> bool {
        self.list_head.is_none()
    }

    pub fn len(&self) -> usize {
        let mut node = &self.list_head;
        let mut i = 0;
        loop {
            match *node {
                Some(ref n) => {
                    i+=1;
                    node=&n.next;
                }
                None => {
                    return i;
                }
            }
        }
    }

    /// Create an empty DList
    pub fn new() -> List<T> {
        List{list_head: None, list_tail: Rawlink::none()}
    }

    pub fn push_front(&mut self, elt: T) {
        self.push_front_node(Box::new(Node::new(elt)))
    }

    pub fn push_front_node(&mut self, mut new_head: Box<Node<T>>) {
        match self.list_head {
            None => {
                self.list_tail = Rawlink::some(&mut new_head);
                new_head.prev = Rawlink::none();
                self.list_head = Some(new_head);
            }
            Some(ref mut head) => {
                new_head.prev = Rawlink::none();
                head.prev = Rawlink::some(&mut new_head);
                mem::swap(head, &mut new_head);
                head.next = Some(new_head);
            }
        }
    }

    /// Provide a forward iterator
    #[inline]
    pub fn iter<'a>(&'a self) -> ListIterator<'a, T> {
        ListIterator{nelem: self.len(), head: &self.list_head, tail: self.list_tail}
    }
}

impl<T> Node<T> {
    fn new(v: T) -> Node<T> {
        Node{value: v, next: None, prev: Rawlink::none()}
    }
}

/// Rawlink is a type like Option<T> but for holding a raw pointer
impl<T> Rawlink<T> {
    /// Like Option::None for Rawlink
    fn none() -> Rawlink<T> {
        Rawlink{p: ptr::null_mut()}
    }

    /// Like Option::Some for Rawlink
    fn some(n: &mut T) -> Rawlink<T> {
        Rawlink{p: n as *mut T}
    }

    /// Convert the `Rawlink` into an Option value
    fn resolve_immut<'a>(&self) -> Option<&'a T> {
        unsafe { self.p.as_ref() }
    }

    /// Convert the `Rawlink` into an Option value
    fn resolve<'a>(&mut self) -> Option<&'a mut T> {
        unsafe { self.p.as_mut() }
    }

    /// Return the `Rawlink` and replace with `Rawlink::none()`
    fn take(&mut self) -> Rawlink<T> {
        mem::replace(self, Rawlink::none())
    }
}

pub struct ListIterator<'a, T: 'a> {
    head: &'a Option<Box<Node<T>>>,
    tail: Rawlink<Node<T>>,
    nelem: usize,
}

impl<'a, A> Iterator for ListIterator<'a, A> {
    type Item = &'a A;

    #[inline]
    fn next(&mut self) -> Option<&'a A> {
        if self.nelem == 0 {
            return None;
        }
        self.head.as_ref().map(|head| {
            self.nelem -= 1;
            self.head = &head.next;
            &head.value
        })
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.nelem, Some(self.nelem))
    }
}

impl<'a, A> DoubleEndedIterator for ListIterator<'a, A> {
    #[inline]
    fn next_back(&mut self) -> Option<&'a A> {
        if self.nelem == 0 {
            return None;
        }
        let tmp = self.tail.resolve_immut();
        tmp.as_ref().map(|prev| {
            self.nelem -= 1;
            self.tail = prev.prev;
            &prev.value
        })
    }
}

fn main() {
}

Playground

Rust相关问答推荐

为什么`Vec i64`的和不知道是`Option i64`?

使用模块中的所有模块,但不包括特定模块

在不重写/专门化整个函数的情况下添加单个匹配手臂到特征的方法?

定义采用更高级类型泛型的性状

无法将记录器向下转换回原始 struct

如何使用盒装枚举进行模式匹配?

如何在Rust中基于字符串 Select struct ?

如何在Rust中缩短数组

为什么 vec![Vec::with_capacity(n)] 为子向量创建 0 容量?

为什么编译器看不到这个 `From` impl?

为什么我的trait 对象类型不匹配?

部署Rust发布二进制文件的先决条件

Rust并发读写引起的死锁问题

在不安全的 Rust 中存储对 struct 内部数据的静态引用是否合法?

如何从 rust 中的同一父目录导入文件

按下 Ctrl + C 时优雅地停止命令并退出进程

将 Futures 的生命周期特征绑定到 fn 参数

为什么具有 Vec 变体的枚举没有内存开销?

在 Rust 中返回对枚举变体的引用是个好主意吗?

为什么 match 语句对引用类型比函数参数更挑剔?