How to fix "types differ in mutability"

trait WatchTrait {
    fn update(&self);
  }

  struct Watch {
    name: String,
  }

  impl Watch {
    fn new(name: String) -> Self {
      Self { name }
    }
  }

  impl WatchTrait for Watch {
    fn update(&self) {
      println!(">>>>>>>>>>>>>>>>>{}", self.name)
    }
  }

  trait Observer4T {
    fn emit(&mut self, name: String, callback: Box<dyn WatchTrait + 'static>);
    fn on(&mut self, name: String);
  }

  struct Observer4<'a> {
    list: Vec<&'a mut (String, Vec<Box<dyn WatchTrait + 'static>>)>,
  }

  impl<'a> Observer4<'a> {
    fn new() -> Self {
      Self { list: Vec::new() }
    }
  }
  impl<'a> Observer4T for Observer4<'a> {
    fn emit(&mut self, name: String, callback: Box<dyn WatchTrait + 'static>) {
      let mut index = false;
      for (_i, item) in self.list.iter().enumerate() {
        if item.0 == name {
          item.1.push(callback);
          index = true;
          // index = i as i32
        }
      }
      if !index {
        let mut k = Vec::new();
        k.push(callback);
        let mut l = &(name, k);
        self.list.push(l);
      }
    }
    fn on(&mut self, name: String) {
      for item in self.list.iter() {
        if item.0 == name {
          for t in item.1.iter() {
            t.update()
          }
        }
      }
    }
  }

  #[test]
  fn execute4() {
    let mut o = Observer4::new();
    o.emit("noe".to_string(), Box::new(Watch::new("name".to_string())));
    o.emit("noe".to_string(), Box::new(Watch::new("3232".to_string())));
    o.on("noe".to_string())
  }

Compiling rust_desifn_patter v0.1.0 (/Users/wuls/Desktop/github/rust_desifn_patter)
error[E0308]: mismatched types
--> src/observer.rs:302:24
|
302 | self.list.push(l);
| ^ types differ in mutability
|
= note: expected mutable reference &'a mut (String, Vec<Box<(dyn WatchTrait + 'static)>>)
found reference &(String, Vec<Box<dyn WatchTrait>>)

error: aborting due to previous error

For more information about this error, try rustc --explain E0308.
error: could not compile rust_desifn_patter

how to make it run?

i tried so many time ,but it always failed.

The core problem here is that k is owned by the emit() function. Any reference to it, mutable or not, becomes invalid when the function returns. To make this pattern work, Observer4 needs to own the vectors:

  struct Observer4 {
    list: Vec<(String, Vec<Box<dyn WatchTrait + 'static>>)>,
  }

You then need to slightly refactor the emit function so that you only move callback to one place (in a way that the compiler understands). The easiest way is to put it in a mutable Option, and then use take() to check if you've already moved it:

    fn emit(&mut self, name: String, callback: Box<dyn WatchTrait + 'static>) {
      let mut opt_callback = Some(callback);

      for item in self.list.iter_mut() {
        if item.0 == name {
          if let Some(cb) = opt_callback.take() {
            item.1.push(cb);
          }
        }
      }
      
      if let Some(cb) = opt_callback.take() {
        let mut k = Vec::new();
        k.push(cb);
        let l = (name, k);
        self.list.push(l);
      }
    }
fn emit(&mut self, name: String, callback: Box<dyn WatchTrait>) {
      let mut state = false;
      for item in self.list.iter_mut() {
        if item.0 == name {
          state = true;
          item.1.push(callback);
          // index = i as i32
        }
      }

      if !state {
        let mut k = Vec::new();
        k.push(callback);
        let l = (name, k);
        self.list.push(l);
      }
      

      // match  {
          
      // }
    }

**it can't work **

but

fn emit(&mut self, name: String, callback: Box<dyn WatchTrait>) {
      for item in self.list.iter_mut() {
        if item.0 == name {
          item.1.push(callback);
          return;
          // index = i as i32
        }
      }

      let mut k = Vec::new();
      k.push(callback);
      let l = (name, k);
      self.list.push(l);
    }

it can work

What is the difference between these two codes?

I use the State variable control statement, why is it wrong?

The problems in the first code snippet stem from the limited way that the compiler reasons about your program; it's a good example of code that is "correct" but disallowed by the Rust compiler:

  • Even though it's true, the compiler doesn't understand that no two items in the list share a name, so it complains that callback might be moved in multiple iterations of the loop.
  • Similarly, the compiler doesn't know how to relate a runtime true/false value to control flow. It sees callback moved in the loop and then moved again in the if statement, and doesn't realize that only one of them will ever actually happen.

Using an early return instead means that the function stops as soon as callback is moved, which is something the compiler does understand.

1 Like

First of all, perhaps there are more than one names in your Vec that match. So you could try to assign the callback more than once in the for loop. This is what the error mentioned. So you really meant something like:

        let mut state = false;
        for item in self.list.iter_mut() {
            if item.0 == name {
                state = true;
                item.1.push(callback);
                break;
            }
        }
        // ...

But it still won't work, because when you return, that's control flow that the compiler understands at a deeper level -- it understands the code after the loop will not be ran if callback is consumed. But your mut state is not control flow. Without analyzing runtime behavior, the compiler can't understand that the if block will only be used if the callback hasn't been moved.

Perhaps try refactoring:

    fn emit(&mut self, name: String, callback: Box<dyn WatchTrait>) {
        let maybe_existing = self.list.iter_mut().find(|t| t.0 == name); 
        
        // ...Do whatever...
        
        let vec_ref = match maybe_existing {
            Some(existing) => &mut existing.1,
            None => {
                self.list.push((name, Vec::new()));
                &mut self.list.last_mut().unwrap().1
            }
        };
        
        // ...Do whatever...

        vec_ref.push(callback);
    }

Playground.

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.