rust - Rust 中的可恢复连续传递样式迭代器减少

标签 rust lifetime continuations

我正在尝试编写一个可以在任何时候恢复的连续传递式“减少”函数。我有一个版本可以工作,但是如果我希望它能够利用某种状态的借用,我需要明确地编写一个新版本的函数。 Rust Playground Link

fn reduce_async_with_store<'a, I, A, F, C>(
    store: &mut Store,
    mut iterator: I,
    accumulator: A,
    mut f: F,
    continuation: C,
) where
    I: Iterator + 'a,
    F: FnMut(&mut Store, I::Item, A, Box<dyn FnOnce(&mut Store, A) + 'a>) + Clone + 'a,
    C: FnOnce(&mut Store, A) + 'a,
{
    match iterator.next() {
        None => continuation(store, accumulator),
        Some(item) => {
            let next: Box<dyn FnOnce(&mut Store, A) + 'a> = {
                let f = f.clone();
                Box::new(move |store, accumulator| {
                    reduce_async_with_store(store, iterator, accumulator, f, continuation)
                })
            };
            f(store, item, accumulator, next);
        }
    }
}

fn some_operation(state: &mut Store, continuation: Box<dyn FnOnce(&mut Store) + 'static>) {
    let mut new_state = Store { foo: state.foo };
    continuation(&mut new_state);
}

#[derive(Debug)]
pub struct Store {
    foo: u8,
}

fn main() {
    let mut some_state = Store { foo: 0 };
    let arr = vec![1u8, 2u8, 3u8];
    reduce_async_with_store(
        &mut some_state,
        arr.into_iter(),
        Vec::new(),
        |store, item, mut acc, continuation| {
            println!("Item: {}", item);
            store.foo += item;
            acc.push(item);
            some_operation(
                store,
                Box::new(move |stor| {
                    continuation(stor, acc);
                }),
            );
        },
        |store, acc| {
            println!("Done!! {:?} {:?}", store, acc);
        },
    )
}

这是我想编写的这个函数的版本,我可以在其中将 Store 作为累加器的一部分传入,然后将其取出 - 但是,如果我这样做,我会得到由于需求冲突,无法推断出合适的生命周期

Rust Playground Link

fn reduce_async<'a, I, A, F, C>(mut iterator: I, accumulator: A, mut f: F, continuation: C)
where
    I: Iterator + 'a,
    F: FnMut(I::Item, A, Box<dyn FnOnce(A) + 'a>) + Clone + 'a,
    C: FnOnce(A) + 'a,
{
    match iterator.next() {
        None => continuation(accumulator),
        Some(item) => {
            let next: Box<dyn FnOnce(A) + 'a> = {
                let f = f.clone();
                Box::new(move |accumulator| reduce_async(iterator, accumulator, f, continuation))
            };
            f(item, accumulator, next);
        }
    }
}

fn some_operation(state: &mut Store, continuation: Box<dyn FnOnce(&mut Store) + 'static>) {
    let mut new_state = Store { foo: state.foo };
    continuation(&mut new_state);
}

#[derive(Debug)]
pub struct Store {
    foo: u8,
}

fn main() {
    let mut some_state = Store { foo: 0 };
    let arr = vec![1u8, 2u8, 3u8];
    reduce_async(
        arr.into_iter(),
        (&mut some_state, Vec::new()),
        |item, mut acc, continuation| {
            let (store, vec) = acc;
            println!("Item: {}", item);
            store.foo += item;
            vec.push(item);
            some_operation(
                store,
                Box::new(move |store| {
                    continuation((store, vec));
                }),
            );
        },
        |(store, vec)| {
            println!("Done!! {:?} {:?}", store, vec);
        },
    )
}

我如何编写我的函数的这个非专用版本,并在尊重 Rust 的生命周期的同时传递诸如 &mut Store 之类的东西?

我的第一个例子 reduce_async_with_store 是如何被允许的,即使我没有为 &mut Store 指定一个明确的生命周期,它可以活到 '静态?

some_operation 采用盒装闭包,因为这是我调用的第 3 方 API 函数采用的方式。我想最终用异步迭代器替换此代码,但我使用的库尚不支持 future 。

最佳答案

让我们从some_operation开始;检查常规函数总是比检查闭包更容易,因为编译器只检查它们的签名。

将省略的生命周期倒过来,它看起来像:

fn some_operation<'s>(state: &'s mut Store, continuation: Box<dyn for<'r> FnOnce(&'r mut Store) + 'static>) {
    let mut new_state = Store { foo: state.foo };
    continuation(&mut new_state);
}

涉及两个不同的生命周期:'s'r ——它们之间没有联系。

现在让我们看这里:

Box::new(move |store| {
    continuation((store, vec));
}),

continuation类型应该是 Box<dyn FnOnce(A) + 'a>根据 reduce_async的签名。 A 的类型是什么单态化后?传递给函数的参数是一个元组:

(&mut some_state, Vec::new()),

第一个元素的类型为 &'state mut State对于一些 'state第二个有Vec<u8> .重温some_operation的签名:第一个参数是&'s mut State , 所以我们选择了 'state = 's这里。然后我们使用类型为 &'r mut State 的参数调用闭包。 .

回到主程序,我们尝试从 (&'r mut State, Vec<u8>) 类型的值构建累加器这与 (&'state mut State, Vec<u8>) 不同.

这就是编译器试图解释的内容 :) 让我们通过更改 some_operation 来检查这个解释的签名:

fn some_operation<'s>(state: &'s mut Store, continuation: Box<dyn FnOnce(&'s mut Store) + 's>) {
    continuation(state);
}

这里我们明确标记两个生命周期应该相同,现在代码编译没有任何错误。

请注意,您的第一个代码片段没有问题,因为 store: &mut Store 的生命周期每次调用 reduce_async_with_store 时参数都不同!在第二个片段中,它被固定为 'state .

在我看来,最简单的解决方法是完全删除可变引用并传递 Store通过转让所有权。

Rust playground link

fn reduce_async<'a, I, A, F, C>(mut iterator: I, accumulator: A, mut f: F, continuation: C)
where
    I: Iterator + 'a,
    F: FnMut(I::Item, A, Box<dyn FnOnce(A) + 'a>) + Clone + 'a,
    C: FnOnce(A) + 'a,
{
    match iterator.next() {
        None => continuation(accumulator),
        Some(item) => {
            let next: Box<dyn FnOnce(A) + 'a> = {
                let f = f.clone();
                Box::new(move |accumulator| reduce_async(iterator, accumulator, f, continuation))
            };
            f(item, accumulator, next);
        }
    }
}

fn some_operation(state: Store, continuation: Box<dyn FnOnce(Store) + 'static>) {
    let new_state = Store { foo: state.foo };
    continuation(new_state);
}

#[derive(Debug)]
pub struct Store {
    foo: u8,
}

fn main() {
    let some_state = Store { foo: 0 };
    let arr = vec![1u8, 2u8, 3u8];
    reduce_async(
        arr.into_iter(),
        (some_state, Vec::new()),
        |item, acc, continuation| {
            let (mut store, mut vec) = acc;
            println!("Item: {}", item);
            store.foo += item;
            vec.push(item);
            some_operation(
                store,
                Box::new(move |store| {
                    continuation((store, vec));
                }),
            );
        },
        |(store, vec)| {
            println!("Done!! {:?} {:?}", store, vec);
        },
    )
}

请记住,连续调用不是尾递归的,因此堆栈会在每次迭代时增长。您可能需要一个蹦床。

关于rust - Rust 中的可恢复连续传递样式迭代器减少,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56463637/

相关文章:

asynchronous - 使用异步 (tokio) rust-websocket 在客户端之间共享可变状态

dynamic - 如何在actix_web/rust的http响应中流式传输非静态字节?

rust - 仅当两个变量不为 None 而不复制它们时,如何执行操作?

haskell - 使用延续monad在 `Set`(和其他具有约束的容器)上构造有效的monad实例

rust - “alias” 相对于 `Sync` 特征意味着什么?

rust - 借用的值(value)活得不够长,需要静态生命周期

Rust:错误 [E0495]:由于需求冲突,无法推断出 autoref 的适当生命周期

rust - 为什么闭包的可变引用参数不会比函数调用更长久?

scala - 在延续中推断结果类型

haskell - 将 Scheme call/cc 翻译成 Haskell callCC