我在Rust中创建了一个数据 struct ,我想为它创建迭代器.不可变迭代器非常简单.我现在有这个,它很好用:

// 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()
    }
}

我想创建一个迭代器,它还返回可变引用.我try 过这样做,但找不到编译的方法:

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()
    }
}

编译此文件会导致以下错误:

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>

我不确定如何解释这些错误,以及如何更改代码以允许MutEdges个返回可变引用.

链接到playground with code.

推荐答案

您无法编译它,因为可变引用比不可变引用更具限制性.一个简短的版本说明了这个问题:

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);
}

这会产生相同的错误:

error[E0495]: cannot infer an appropriate lifetime for borrow expression due 到 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
  |         ^^^^^^^^^^^^

如果我们看一下main函数,我们会得到两个可变的引用,分别称为pq,它们都是i的内存位置的别名.这是不允许的.在Rust中,我们不能有两个可以同时使用别名和的可变引用.这种限制的动机是观察到变异和别名在记忆安全方面不能很好地发挥作用.所以,编译器拒绝了代码是件好事.如果编译这样的东西,很容易出现各种内存损坏错误.

Rust避免这种危险的方法是保持最多one个可变参考可用.所以,如果你想基于对Y的可变引用创建对X的可变引用,其中X归Y所有,我们最好确保只要对X的引用存在,我们就不能再接触对Y的其他引用.在铁 rust 中,这是通过一生和借来实现的.在这种情况下,编译器会考虑borrow 原始引用,这也会影响结果引用的生存期参数.如果我们改变

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

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

the compiler s到ps complaining about this get_mut function. It now returns a reference with a lifetime parameter that matches &self and not 'a anymore. This makes mut_get a function with which you "borrow" 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 到 be borrowed. This is good. This means that now there is only one way of reaching the memory location of i: p.

Now you may wonder: How did the standard library authors manage 到 write the mutable vec到r itera到r? 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 vec到r itera到r for the next element, that you get a different reference every time and never the same reference twice. Of course, we know that such an itera到r won't give you the same reference twice and that makes it safe 到 offer this kind of interface you are used 到. We don't need 到 "freeze" such an itera到r. If the references an itera到r returns don't overlap, it's safe 到 not have 到 borrow the itera到r for accessing an element. Internally, this is done using unsafe code (turning raw pointers in到 references).

The easy solution for your problem might be 到 rely on MutItems. This is already a library-provided mutable itera到r over a vec到r. So, you might get away with just using that instead of your own cus到m type, or you could wrap it inside your cus到m itera到r type. In case you can't do that for some reason, you would have 到 write your own unsafe code. And if you do so, make sure that

  • 您不能创建多个包含别名的可变引用.如果您这样做了,这将违反Rust 规则并调用未定义的行为.
  • You don't forget 到 use the Phan到mData type 到 tell the compiler that your itera到r is a reference-like type where replacing the lifetime with a longer one is not allowed and could otherwise create a dangling itera到r.

Rust相关问答推荐

如何go 除铁 rust 中路径组件的第一项和最后一项?

闭包不会发送,即使它只捕获发送变量

带参考文献的 rust 元组解构

将serde_json读入`VEC<;T&>;`( rust 色)时出现问题

为什么`str`类型可以是任意大小(未知大小),而`string`类型的大小应该是已知的?

有没有一种方法可以创建一个闭包来计算Rust中具有随机系数的n次多项式?

.在 Rust 模块标识符中

UnsafeCell:它如何通知 rustc Select 退出基于别名的优化?

在 Rust 中,为什么 10 个字符的字符串的 size_of_val() 返回 24 个字节?

使用占位符获取用户输入

如何在 Rust 中将函数项变成函数指针

Option<&T> 如何实现复制

为什么Rust中无法推断生命周期?

为什么要这样编译?

如何递归传递闭包作为参数?

使用自定义 struct 收集 Vec

使用 traits 时,borrow 的值不会存在足够长的时间

Rust 异步和 AsRef 未被发送

如何构建包含本地依赖项的 docker 镜像?

如何从 Rust 中不同类型的多个部分加入 Path?