Beginner: how to implement a simple notify in Rust way?

fn main() {
	let mut f1 = Feature { info : 1, features : vec![] };
	let mut f2 = Feature { info : 2, features : vec![] };
	let mut f3 = Feature { info : 3, features : vec![] };

	let list = vec![f1, f2, f3];
	let mut card = Card { features : list };

	f1.features = &list;
	f2.features = &list;
	f3.features = &list;

	f3.change(7);
}

struct Card {
	features : Vec<Feature>
}

struct Feature {
	info : i32,
	features : &Vec<Feature>
}

impl Feature {
	fn notify(&mut self, value : i32) {
		self.info = 99;
	}

	fn change(&mut self, value : i32) {
		self.info = value;
		for &mut f in self.features {
			f.notify(value);
		}
	}
}

This codes apparently can't work for Rust, my question is how to implement a notify in Rust way?
My requirement is notify all elements in a Vec include itself by invoking from the element.

Is that a hard requirement? :slight_smile: Specifically, the "from the element" part. I think in general you'll have an easier time with Rust if you make ownership "linear"/unidirectional, as much as possible.

As a straw man, here's a version that initiates the change from Card (i.e. the owner of features), but allows the target Feature to indicate how other Features should be change:

fn main() {
	let mut card = Card {features: vec![Feature {info: 1}, Feature {info: 2}]};
	card.change_feature(0, 7);
	println!("{:?}", card)
}

#[derive(Debug)]
struct Card {
	features : Vec<Feature>
}

impl Card {
    fn change_feature(&mut self, idx: usize, value: i32) {
        let mut func = self.features[idx].change(value);
        for mut f in &mut self.features.iter_mut().enumerate().filter(|&(x,_)| x != idx) {
            func(f.1);
        }
    }
}

#[derive(Debug)]
struct Feature {
	info : i32
}

impl Feature {
    fn change(&mut self, value: i32) -> Box<FnMut(&mut Feature)> {
        self.info = value;
        Box::new(move |f: &mut Feature| f.info = 99)
    }
}

Otherwise, with what you're trying to do, you'll probably need a healthy dose of Rc and RefCell to make it work (I haven't tried though). And then you lose some of the static borrow soundness guarantees and can get panics at runtime. I don't know what others think, but if I saw a case where Rc and RefCell need to be generously sprinkled, I'd try to rethink the design (if that's an option).

2 Likes

Thank you very much.
That's a great idea :slight_smile: