3

Context

I'm a beginner, and, at a high level, what I want to do is store some mutable state (to power a state machine) in a struct with the following constraints

  • Mutating the state doesn't require the entire struct to be mut (since I'd have to update a ton of callsites to be mut + I don't want every field to be mutable)
  • The state is represented as an enum, and can, in the right state, store a way to index into the correct position in a vec that's in the same struct

I came up with two different approaches/examples that seem quite complicated and I want to see if there's a way to simplify. Here's some playgrounds that minimally reproduce what I'm exploring

  1. Using a Cell and a usize
#[derive(Clone, Copy)]
enum S {
    A,
    B(usize),
}

struct Test {
    a: Vec<i32>,
    b: Cell<S>,
}

where usage look like this

println!("{}", t.a[index]);
t.b.set(S::B(index + 1));
  1. Using a RefCell and an iterator
enum S<'a> {
    A,
    B(Iter<'a, i32>),
}

struct Test<'a> {
    a: Vec<i32>,
    b: RefCell<S<'a>>,
}

where usage looks like this

println!("{}", iter.next().unwrap());

Questions

  1. Is there a better way to model this in general vs. what I've tried?
  2. I like approach #2 with the iterator better in theory since it feels cleaner, but I don't like how it introduces explicit lifetime annotations into the struct...in the actual codebase I'm working on, I'd need to update a ton of callsites to add the lifetime annotation and the tiny bit of convenience doesn't seem worth it. Is there some way to do #2 without introducing lifetimes?
m0meni
  • 16,006
  • 16
  • 82
  • 141
  • I think adding the lifetime annotations should be easily automatable and far better than having to maintain explicit calls to `set` to advance the home-brew iterator. – chepner Apr 20 '22 at 17:26
  • 1
    If the iterator stored in `b` references `a` this would be a self-referential struct, which [can't be easily done in Rust](https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct). Approach #2 is non-viable without pinning. – cdhowie Apr 20 '22 at 17:27
  • What is your struct S for? It seems to act as Option where S::A is None. – hkBst Apr 20 '22 at 17:27
  • @cdhowie hmm I see. How would you approach getting #2 to work? I read through the linked question and it seems like I have a lot of potential options...that being said I don't think keeping the state separate from the struct itself makes sense because it'd require the caller to create the state and pass it in to every function alongside the struct – m0meni Apr 20 '22 at 17:39
  • I don't think this is a duplicate, but this answer might help you understand how you can store a generic iterator: https://stackoverflow.com/a/68278323/5987669 – Locke Apr 20 '22 at 18:00
  • 2
    I wonder if there really is a good reason which makes you want to avoid simply making the whole struct `mut`? If you think there should really be a part of your struct that should be mutable, and an other part that should not be mutable, maybe the best idea would simply be to split the struct into a mutable part and a non-mutable part. Also, you can restrain access to fields without `pub`, and just add a getter method if you really don't want them to be mutated. – jthulhu Apr 20 '22 at 18:02
  • 1
    @m0meni It depends exactly on what the rest of the implementation looks like. For example, if this is a forward-only iterator that never reads past elements then you can just use `Vec::into_iter()` to produce the iterator and not store the `Vec` at all. – cdhowie Apr 20 '22 at 19:11

0 Answers0