我有一个简单的#[pymythods]-impl,以最天真的方式使用&pyo3::types::PyIterator:

/// Construct a `Foo` from an iterator
#[staticmethod]
fn from_iter(iter: &pyo3::types::PyIterator) -> PyResult<Self> {
    for mut foo = Self::new()?;
    for obj in iter {
        foo.bar(obj?)?;
    }
    Ok(foo)
}

我注意到,当迭代器执行时,内存使用量增长不受限制.关于内存管理的pyo3文档似乎特别提到了这种情况,尽管我不清楚我是否正确理解了这个问题:

在我看来,因为我们使用&'a PyIterator进入函数,所以我们已经持有GIL,其中'a绑定到GIL.由于PyIterator在迭代期间返回&'a PyAny,并且因为这些对象必须对至少'a有效,所以迭代过的对象不会在循环的每一次迭代期间被销毁;因此,内存使用量会增长,直到函数返回并且所有东西都被一举收集.

在循环过程中销毁每个obj的正确策略是什么?文档指向使用unsafe,我不确定上面的简单代码是否真的需要它.

推荐答案

回答我自己的问题:

实际上,解决方案是使用unsafe,如在PYO的S文档on memory management.中所描述的,引入不安全是因为我们需要销毁正在迭代的对象,而解释器无法确定Rust-Part是否秘密地持有它们.

fn from_iter(py: Python, mut iter: &pyo3::types::PyIterator) -> PyResult<Self> {
    let mut foo = Self::new()?
    // Explicit `loop` instead of `for`-loop, so that the `pool`
    // is active *before* `obj` is returned from the `PyIterator`
    loop {
        // SAFETY: We only derive, and then never observe
        // `obj` outside/after each iteration
        let pool = unsafe { py.new_pool() };
        match iter.next() {
            Some(obj) => {
                foo.bar(obj?)?;
            }
            None => {
                break;
            }
            drop(pool); // Explicit for clarity
        }
    }
    Ok(Self { inner: foo })
}

Rust相关问答推荐

为什么复印是豆荚的一个重要特征?

如何创建引用构造函数拥有的变量的对象?

从特征实现调用函数的Rust惯用方法

通过解引用将值移出Box(以及它被脱糖到什么地方)?

如何格式化传入Rust中mysql crate的Pool::new的字符串

创建Rust中元对象协议的动态对象 Select /重新分配机制

如何将映射反序列化为具有与键匹配的字段的定制 struct 的向量?

为什么这个变量不需要是可变的?

变量需要parse()中的显式类型

如何在不调用Collect()的情况下为新型vec实现IntoIterator?

这是什么:`impl Trait for T {}`?

Windows 上 ndarray-linalg 与 mkl-stats 的链接时间错误

使用占位符获取用户输入

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

第 7.4 章片段中如何定义 `thread_rng`

`use std::error::Error` 声明中断编译

我如何将特征作为 struct 的拥有字段?

为什么 no_std crate 可以依赖于使用 std 的 crate?

Rust:如果我知道只有一个实例,那么将可变borrow 转换为指针并返回(以安抚borrow 判断器)是否安全?

为什么-x试图解析为文字并在声明性宏中失败?