简体   繁体   English

如何使用返回可变引用的迭代器创建自己的数据结构?

[英]How can I create my own data structure with an iterator that returns mutable references?

I have created a data structure in Rust and I want to create iterators for it.我在 Rust 中创建了一个数据结构,我想为它创建迭代器。 Immutable iterators are easy enough.不可变迭代器很容易。 I currently have this, and it works fine:我目前有这个,它工作正常:

// This is a mock of the "real" EdgeIndexes class as
// the one in my real program is somewhat complex, but
// of identical type

struct EdgeIndexes;

impl Iterator for EdgeIndexes {
    type Item = usize;
    fn next(&mut self) -> Option<Self::Item> {
        Some(0)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, None)
    }
}

pub struct CGraph<E> {
    nodes: usize,
    edges: Vec<E>,
}

pub struct Edges<'a, E: 'a> {
    index: EdgeIndexes,
    graph: &'a CGraph<E>,
}

impl<'a, E> Iterator for Edges<'a, E> {
    type Item = &'a E;

    fn next(&mut self) -> Option<Self::Item> {
        match self.index.next() {
            None => None,
            Some(x) => Some(&self.graph.edges[x]),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.index.size_hint()
    }
}

I want to create a iterator that returns mutable references as well.我想创建一个返回可变引用的迭代器。 I've tried doing this, but can't find a way to get it to compile:我试过这样做,但找不到编译的方法:

pub struct MutEdges<'a, E: 'a> {
    index: EdgeIndexes,
    graph: &'a mut CGraph<E>,
}

impl<'a, E> Iterator for MutEdges<'a, E> {
    type Item = &'a mut E;

    fn next(&mut self) -> Option<&'a mut E> {
        match self.index.next() {
            None => None,
            Some(x) => self.graph.edges.get_mut(x),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.index.size_hint()
    }
}

Compiling this results in the following error:编译它会导致以下错误:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/lib.rs:54:24
   |
54 |             Some(x) => self.graph.edges.get_mut(x),
   |                        ^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 51:5...
  --> src/lib.rs:51:5
   |
51 | /     fn next(&mut self) -> Option<&'a mut E> {
52 | |         match self.index.next() {
53 | |             None => None,
54 | |             Some(x) => self.graph.edges.get_mut(x),
55 | |         }
56 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:54:24
   |
54 |             Some(x) => self.graph.edges.get_mut(x),
   |                        ^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 48:6...
  --> src/lib.rs:48:6
   |
48 | impl<'a, E> Iterator for MutEdges<'a, E> {
   |      ^^
   = note: ...so that the expression is assignable:
           expected std::option::Option<&'a mut E>
              found std::option::Option<&mut E>

I'm unsure how to interpret these errors and how to change my code in order to allow MutEdges to return mutable references.我不确定如何解释这些错误以及如何更改我的代码以允许MutEdges返回可变引用。

Link to playground with code . 使用代码链接到游乐场

You can't compile this because mutable references are more restrictive than immutable references.您无法编译它,因为可变引用比不可变引用更具限制性。 A shortened version that illustrates the issue is this:说明问题的缩短版本是这样的:

struct MutIntRef<'a> {
    r: &'a mut i32
}

impl<'a> MutIntRef<'a> {
    fn mut_get(&mut self) -> &'a mut i32 {
        &mut *self.r
    }
}

fn main() {
    let mut i = 42;
    let mut mir = MutIntRef { r: &mut i };
    let p = mir.mut_get();
    let q = mir.mut_get();
    println!("{}, {}", p, q);
}

Which produces the same error:这会产生相同的错误:

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
 --> src/main.rs:7:9
  |
7 |         &mut *self.r
  |         ^^^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 6:5...
 --> src/main.rs:6:5
  |
6 | /     fn mut_get(&mut self) -> &'a mut i32 {
7 | |         &mut *self.r
8 | |     }
  | |_____^
note: ...so that reference does not outlive borrowed content
 --> src/main.rs:7:9
  |
7 |         &mut *self.r
  |         ^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 5:6...
 --> src/main.rs:5:6
  |
5 | impl<'a> MutIntRef<'a> {
  |      ^^
note: ...so that reference does not outlive borrowed content
 --> src/main.rs:7:9
  |
7 |         &mut *self.r
  |         ^^^^^^^^^^^^

If we take a look at the main function, we get two mutable references called p and q that both alias the memory location of i .如果我们看一下 main 函数,我们会得到两个可变引用,称为pq ,它们都是i的内存位置的别名。 This is not allowed.这是不允许的。 In Rust, we can't have two mutable references that alias and are both usable.在 Rust 中,我们不能有两个可变引用作为别名并且都可用。 The motivation for this restriction is the observation that mutation and aliasing don't play well together with respect to memory safety.这种限制的动机是观察到突变和别名在内存安全方面不能很好地协同工作。 So, it's good that the compiler rejected the code.所以,编译器拒绝代码是件好事。 If something like this compiled, it would be easy to get all kinds of memory corruption errors.如果编译这样的东西,很容易出现各种内存损坏错误。

The way Rust avoids this kind of danger is by keeping at most one mutable reference usable. Rust 避免这种危险的方法是保持最多一个可变引用可用。 So, if you want to create mutable reference to X based on a mutable reference to Y where X is owned by Y, we better make sure that as long as the reference to X exists, we can't touch the other reference to Y anymore.因此,如果您想基于对 Y 的可变引用创建对 X 的可变引用,其中 X 归 Y 所有,我们最好确保只要对 X 的引用存在,我们就不能再接触对 Y 的另一个引用. In Rust this is achieved through lifetimes and borrowing.在 Rust 中,这是通过生命周期和借用来实现的。 The compiler considers the original reference to be borrowed in such a case and this has an effect on the lifetime parameter of the resulting reference as well.在这种情况下,编译器会考虑借用原始引用,这也会影响结果引用的生命周期参数。 If we change如果我们改变

fn mut_get(&mut self) -> &'a mut i32 {
    &mut *self.r
}

to

fn mut_get(&mut self) -> &mut i32 { // <-- no 'a anymore
    &mut *self.r // Ok!
}

the compiler stops complaining about this get_mut function.编译器不再抱怨这个get_mut函数。 It now returns a reference with a lifetime parameter that matches &self and not 'a anymore.它现在返回一个带有匹配&self而不是'a的生命周期参数的引用。 This makes mut_get a function with which you "borrow" self .这使mut_get成为您“借用” self的函数。 And that's why the compiler complains about a different location:这就是编译器抱怨不同位置的原因:

error[E0499]: cannot borrow `mir` as mutable more than once at a time
  --> src/main.rs:15:13
   |
14 |     let p = mir.mut_get();
   |             --- first mutable borrow occurs here
15 |     let q = mir.mut_get();
   |             ^^^ second mutable borrow occurs here
16 |     println!("{}, {}", p, q);
   |                        - first borrow later used here

Apparently, the compiler really did consider mir to be borrowed.显然,编译器确实考虑mir是借用。 This is good.这很好。 This means that now there is only one way of reaching the memory location of i : p .这意味着现在只有一种方法可以到达i : p的内存位置。

Now you may wonder: How did the standard library authors manage to write the mutable vector iterator?现在您可能想知道:标准库作者是如何设法编写可变向量迭代器的? The answer is simple: They used unsafe code.答案很简单:他们使用了不安全的代码。 There is no other way.没有其他办法。 The Rust compiler simply does not know that whenever you ask a mutable vector iterator for the next element, that you get a different reference every time and never the same reference twice. Rust 编译器根本不知道,每当您向可变向量迭代器询问下一个元素时,您每次都会获得不同的引用,而不会两次获得相同的引用。 Of course, we know that such an iterator won't give you the same reference twice and that makes it safe to offer this kind of interface you are used to.当然,我们知道这样的迭代器不会给你两次相同的引用,这使得提供你习惯的这种接口是安全的。 We don't need to "freeze" such an iterator.我们不需要“冻结”这样的迭代器。 If the references an iterator returns don't overlap, it's safe to not have to borrow the iterator for accessing an element.如果迭代器返回的引用不重叠,则不必借用迭代器来访问元素是安全的。 Internally, this is done using unsafe code (turning raw pointers into references).在内部,这是使用不安全代码(将原始指针转换为引用)来完成的。

The easy solution for your problem might be to rely on MutItems .您的问题的简单解决方案可能是依赖MutItems This is already a library-provided mutable iterator over a vector.这已经是一个库提供的向量可变迭代器。 So, you might get away with just using that instead of your own custom type, or you could wrap it inside your custom iterator type.因此,您可能只使用它而不是您自己的自定义类型,或者您可以将它包装在您的自定义迭代器类型中。 In case you can't do that for some reason, you would have to write your own unsafe code.如果由于某种原因您不能这样做,您将不得不编写自己的不安全代码。 And if you do so, make sure that如果你这样做,请确保

  • You don't create multiple mutable references that alias.您不会创建多个该别名的可变引用。 If you did, this would violate the Rust rules and invoke undefined behavior.如果你这样做了,这将违反 Rust 规则并调用未定义的行为。
  • You don't forget to use the PhantomData type to tell the compiler that your iterator is a reference-like type where replacing the lifetime with a longer one is not allowed and could otherwise create a dangling iterator.您不要忘记使用PhantomData类型来告诉编译器您的迭代器是一种类似引用的类型,其中不允许用更长的生命周期替换生命周期,否则可能会创建一个悬空迭代器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM