I want to have an iterator which returns an Item
that needs to affect the subsequent items of the iterator.
My current scenario is this
enum Response {
TaskToDo(Function)
Text(String)
}
enum Step {
StepInitial,
StepX(InitialStepResult),
StepY,
StepZ,
}
struct Foo {
last_step: Step,
}
impl Foo {
fn new() -> Self {
Foo { last_step: Step::StepInitial }
}
}
impl Iterator for Foo {
type Item = Response;
fn next(&mut self) -> Option<Self::Item> {
match self.last_step {
StepInitial => handle_initial(self),
StepX(initial_step_result) => handle_x(self, initial_step_result),
StepY => handle_y(self),
StepZ => handle_z(self),
}
}
}
fn handle_initial(&mut Foo) -> Response {
...
self.last_step = StepX(initial_step_result);
Response::TaskToDo(|| {
...
initial_step_result.somehow_mark_successful();
})
}
fn handle_x(&mut Foo, result: InitialStepResult) ->(Response {
...
if initial_step_result.is_successful() {
self.last_step = StepY;
Response::Text("Entered Step Y")
}
else {
self.last_step = StepZ;
Response::Text("Entered Step Z")
}
}
fn main() {
let mut foo = Foo::new();
for item in foo {
match item {
Response::Text(text) => {
println!("Thing = {}", text);
}
Response::Todo(mut func) => {
func(&mut foo);
}
}
}
}
In this snippet InitialStepResult
is a hypothetical type. The Response::TaskToDo(function)
needs to somehow communicate back success or failure to Foo
using it. Foo
will then go to StepY
or StepZ
based on that.
How do I achieve the behavior of InitialStepResult
? Can it be a Rc<RefCell<Result<_, _>>>
or some way to pass a message like channel in futures::channel::oneshot - Rust ?
Add some more state to Foo
. Make the function that is returned for StepX
call into a method of Foo
that would mutate the state. Use that state to decide what the next step would be.
That would require the function to hold a mutable reference to foo, yes? Is that possible, for an item from an iterator to hold a mutable reference to the iterator?
fprasx
July 13, 2022, 5:11pm
4
I don't think it should be this complicated. You already have a mutable reference to Foo, so you can just use that to modify it.
You can add a field on Foo
like so:
struct Foo {
last_step: Step,
success: bool // hi :)
}
success
starts as false
:
impl Foo {
fn new() -> Self {
Foo { last_step: Step::StepInitial, success: false}
}
}
And then once you perform the StepInitial
, you can change the result like so:
fn handle_initial(&mut Foo) -> Response {
...
self.last_step = StepX(initial_step_result);
Response::TaskToDo(|| {
...
initial_step_result.somehow_mark_successful(); // --
if successful { // ++
foo.success = true // ++
} // ++
})
}
And then you can use that later on.
2 Likes
The following is an example of what I think you want:
#[derive(Default)]
struct Foo {
next_step: u32,
is_successful: bool,
}
enum Response {
Text(String),
Todo(Box<dyn FnMut(&mut Foo)>),
}
impl Iterator for Foo {
type Item = Response;
fn next(&mut self) -> Option<Self::Item> {
if self.next_step == 0 {
Some(Response::Todo(Box::new(|foo: &mut Foo| {
foo.is_successful = true;
foo.next_step += 1;
})))
} else if self.is_successful && self.next_step < 5 {
let step = self.next_step;
self.next_step += 1;
Some(Response::Text(format!("{}", step)))
} else {
None
}
}
}
fn main() {
let mut foo = Foo::default();
while let Some(item) = foo.next() {
match item {
Response::Text(text) => {
println!("Thing = {}", text);
}
Response::Todo(mut func) => {
func(&mut foo);
}
}
}
}
2 Likes
Ok so it works fine by calling next
, but not when I do for item in foo
because that implicitly calls into_iter
I'll update my example to add this requirement.
You can change the data types to Rc<Cell<_>>
(or Arc<AtomicBool>
and Arc<AtomicU32>
, or Arc<Mutex<_>>
), and when constructing the closure, capture clones of those instead of taking a &mut Foo
parameter.
Playground.
1 Like
Thanks, I'll go with this, as in Arc<Mutex<_>>
One last thing, How would I make the closure async?
I believe something like this, but async
isn't my area. Someone else may have a better answer.
1 Like
system
Closed
October 11, 2022, 8:36pm
11
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.