Problems with iterators


#1

I’m trying to get my program compiled but I have problems with some functions returning Iterator or using them.
The program is pretty long, so I will write here only pieces I think are interested. The full program is available at http://git.savannah.nongnu.org/cgit/lcs.git/tree/
First, the set of functions

pub fn changed_ch_vals(&'a mut self) -> Map<Filter<Map<Iter<'a, Arc<Mutex<Channel>>>, fn(&Arc<Mutex<Channel>>) -> &Channel>, fn(&&Channel)->bool>, fn(&Channel)->ChVal> {
    self.needs_update = false;
    self.channels.iter()
        .map(arc_to_channel as _)
        .filter(ch_is_changed_adapter as _)
        .map(Channel::get_ch_val)
}

fn arc_to_channel<'a> (r: &'a Arc<Mutex<Channel>>) -> &'a Channel{
    r.lock().unwrap().deref()
}
fn ch_is_changed_adapter(r: &&Channel) -> bool{
    (*r).is_changed()
}

in the devs module doesn’t compile with error: borrowed value does not live long enough at the arc_to_channel function.

Second, in the upthread module, there is a loop in a thread:

for ChVal(ch, val) in self.devs.iter()
      .map(|lock| lock.read().unwrap())
      .filter(|d| d.is_changed())
      .flat_map(|d| d.changed_ch_vals()) {
            //send couple to microcontroller
             write!(&mut port, "{}c{}v", ch, val);
},

that gives error: d does not live long enough and error: cannot borrow immutable borrowed content as mutable to the flat_map.

I can’t understand what the problem is. I’m new to Rust and this is my first “serious” project with it.

Thank you for your help.


#2

There are three releases (channels) of the Rust compiler: Stable, Beta and Nightly (Alpha). What version are you using?


#3

I am using
cargo 0.11.0-nightly (259324c 2016-05-20)
rustc 1.10.0 (cfcb716cf 2016-07-03)
which are delivered in the Ubuntu Repository


#4

You arc_to_channel function cannot exist. Looking at its signature:

fn arc_to_channel<'a> (r: &'a Arc<Mutex<Channel>>) -> &'a Channel;

If it compiled, it would mean you could access the interior of the mutex without holding a lock (you can keep the returned reference as long as you want!), which is exactly what Mutex tries to prevent.

The solution would be to make the code slightly less functional, and combine maps and filters into one function:

        self.channels.iter()
            .filter_map(|arc| {
                let channel = arc.lock().unwrap();
                if r.is_changed() {
                    Some(channel.get_ch_val())
                } else {
                    None
                }
            })

Unfortunately, that would require you to Box the returned iterator or use the -> impl Iterator feature from nightly. I guess you’ve avoided the closures here, so you can express the iterator’s type. You can also put this closure to a function, if you want.

(Alternatively, you can make your functions operate on MutexGuard instead of &Channel, but that would compicate your signatures even more)

Speaking of your second error, the problem is that changed_ch_vals borrows self mutably (I guess that’s because of the self.needs_update = false;). Unfortunately, that kind of signature means that the mutable borrow will be held as long as the returned value is alive (the 'a appears both near self and inside a returned value). Since you call that method inside a flat_map, that would mean multiple mutable borrows would occur.

The simplest solution would be just to write a nested for loop instead of the flat_map. The less simple would be to change &mut self to &self, put needs_update into a Cell. The closure inside of the flat_map also needs to be marked as move, so it captures d by value, not reference. (move |d| ...)


#5

Didn’t know about filter_map, now I think it’s very useful sometimes.
For the second error I found that I was locking a RwLock in read mode, so obviously I couldn’t modify it.
Anyway I did a nested for loop and separated the function that returns the iterator from the function that set needs_update to false in order not to hide the mutation.

Thank you for your precious help!