rust - Problem with lifetimes when implementing custom mutable Iterator for struct - Stack Overflow

I want to implement a mutable Iterator for my custom struct as following:#[derive(Debug, Clone)]pub s

I want to implement a mutable Iterator for my custom struct as following:

#[derive(Debug, Clone)]
pub struct Message<'a> {
    content: &'a str,
}

#[derive(Debug, Clone)]
pub struct Messages<'a> {
    data: Vec<Message<'a>>
}

impl<'a> Messages<'a> {
    // explicit lifetime 'a is required here for `&mut self`
    // to disallow the invariance
    pub fn iter_mut(&'a mut self) -> IterMut {
        let messages = self.data.as_mut_slice();
        IterMut { messages }
    }
}

struct IterMut<'a> {
    // store a slice of `Message` items to allow returning
    // a mutable reference to particular Message in `next`
    messages: &'a mut [Message<'a>],
}

impl<'a> Iterator for IterMut<'a> {
    type Item = &'a mut Message<'a>;
    
    fn next(&mut self) -> Option<Self::Item> {
        let messages = &mut self.messages;
        let messages = mem::replace(messages, &mut []);
        let (elem, rest) = messages.split_first_mut()?;
        self.messages = rest;
        Some(elem)
    }
}

When testing the code above, I've faced with a problem: when IterMut is obtained once via iter_mut call on some Messages instance, this instance will be mutably borrowed until the end of the scope:

#[test]
fn test_iter_mut() {
    let mut messages = Messages {
        data: vec![
            Message { content: "A" },
            Message { content: "B" },
        ]
    };
    for msg in messages.iter_mut() {
        *msg = Message { content: "C" }
    }

    // error[E0502]: cannot borrow `messages` as immutable because it is also borrowed as mutable
    println!("Tasks: {:?}", messages);
}

Generally, it looks like the problem is I use the same 'a lifetime everywhere, but an attempt to solve it by entering 2nd 'b lifetime and using it as:

struct IterMut<'a, 'b> {
    messages: &'b mut [Message<'a>],
}

impl<'a, 'b> Iterator for IterMut<'a, 'b> {
    type Item = &'b mut Message<'a>;
    ...
}

did not succeed: the Compiler error is the same as previously ("swapping" 'a and 'b lifetimes doesn't work as well)

NOTE: i know that if I would use struct Message {content: String} instead of struct Message<'a> {content: &'a str} there will be no the same problem with lifetimes, but it's interesting for me how to solve it in current form.

Will be very pleasant for help!

I want to implement a mutable Iterator for my custom struct as following:

#[derive(Debug, Clone)]
pub struct Message<'a> {
    content: &'a str,
}

#[derive(Debug, Clone)]
pub struct Messages<'a> {
    data: Vec<Message<'a>>
}

impl<'a> Messages<'a> {
    // explicit lifetime 'a is required here for `&mut self`
    // to disallow the invariance
    pub fn iter_mut(&'a mut self) -> IterMut {
        let messages = self.data.as_mut_slice();
        IterMut { messages }
    }
}

struct IterMut<'a> {
    // store a slice of `Message` items to allow returning
    // a mutable reference to particular Message in `next`
    messages: &'a mut [Message<'a>],
}

impl<'a> Iterator for IterMut<'a> {
    type Item = &'a mut Message<'a>;
    
    fn next(&mut self) -> Option<Self::Item> {
        let messages = &mut self.messages;
        let messages = mem::replace(messages, &mut []);
        let (elem, rest) = messages.split_first_mut()?;
        self.messages = rest;
        Some(elem)
    }
}

When testing the code above, I've faced with a problem: when IterMut is obtained once via iter_mut call on some Messages instance, this instance will be mutably borrowed until the end of the scope:

#[test]
fn test_iter_mut() {
    let mut messages = Messages {
        data: vec![
            Message { content: "A" },
            Message { content: "B" },
        ]
    };
    for msg in messages.iter_mut() {
        *msg = Message { content: "C" }
    }

    // error[E0502]: cannot borrow `messages` as immutable because it is also borrowed as mutable
    println!("Tasks: {:?}", messages);
}

Generally, it looks like the problem is I use the same 'a lifetime everywhere, but an attempt to solve it by entering 2nd 'b lifetime and using it as:

struct IterMut<'a, 'b> {
    messages: &'b mut [Message<'a>],
}

impl<'a, 'b> Iterator for IterMut<'a, 'b> {
    type Item = &'b mut Message<'a>;
    ...
}

did not succeed: the Compiler error is the same as previously ("swapping" 'a and 'b lifetimes doesn't work as well)

NOTE: i know that if I would use struct Message {content: String} instead of struct Message<'a> {content: &'a str} there will be no the same problem with lifetimes, but it's interesting for me how to solve it in current form.

Will be very pleasant for help!

Share Improve this question asked Mar 11 at 8:46 AlexAlex 496 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 4

Generally, it looks like the problem is I use the same 'a lifetime everywhere, but an attempt to solve it by entering 2nd 'b lifetime [...] did not succeed: the Compiler error is the same as previously ("swapping" 'a and 'b lifetimes doesn't work as well).

You are correct. The problem was that you used the same lifetime for the reference to the Vec, and Message's contents. To solve it you indeed need to introduce a second lifetime. I don't know why it didn't work for you. At the bottom of this answer I posted a working version, which was only modified by adding explicit second lifetime.

More intriguing question is: Why does your first snippet not work? The short answer is that you created &'a mut &'a reference (modulo wrappers) which almost never is a correct thing and most of the time will lead to lifetime-related compiler errors.

The compiler's error isn't very good at pointing the reason of why you cannot borrow messages as immutable while it is also borrowed as a mutable reference, but this is partly, because you have mislead the compiler.

By making iter_mut signature as follows:


impl<'a> Messages<'a> {
    // You elided the lifetime of IterMut here, but it is really: IterMut<'a>
    pub fn iter_mut(&'a mut self) -> IterMut { ... }
}

you are telling compiler that when you call iter_mut it assume that the lifetime of the message (&str) is the same as the lifetime of the borrow of self (Messages). But in fact they are unrelated (to be strict there is a relation between them 'message: 'slice, but it doesn't need to be expressed by any type constraints). So in your test, when you iterate over messages compiler sees that the lifetime of a Message is 'static, so it deduces that it must borrow messages for 'static as well. But that means, that you cannot borrow it ever again.

So the takeaway for this should be:

  • never create &'a mut &'a (unless it actually is the correct thing)
  • if lifetimes don't need to be in a relation, make them separate
  • do not elide lifetimes in returned types (your code violates elided_named_lifetimes lint)

#[derive(Debug, Clone)]
pub struct Message<'a> {
    content: &'a str,
}

#[derive(Debug, Clone)]
pub struct Messages<'a> {
    data: Vec<Message<'a>>
}

impl<'a> Messages<'a> {
    // explicit lifetime 'a is required here for `&mut self`
    // to disallow the invariance
    pub fn iter_mut<'slice>(&'slice mut self) -> IterMut<'slice, 'a> {
        let messages = self.data.as_mut_slice();
        IterMut { messages }
    }
}

pub struct IterMut<'slice, 'message> {
    // store a slice of `Message` items to allow returning
    // a mutable reference to particular Message in `next`
    messages: &'slice mut [Message<'message>],
}

impl<'slice, 'message> Iterator for IterMut<'slice, 'message> {
    type Item = &'slice mut Message<'message>;
    
    fn next(&mut self) -> Option<Self::Item> {
        let messages = &mut self.messages;
        let messages = std::mem::replace(messages, &mut []);
        let (elem, rest) = messages.split_first_mut()?;
        self.messages = rest;
        Some(elem)
    }
}

#[test]
fn test_iter_mut() {
    let mut messages = Messages {
        data: vec![
            Message { content: "A" },
            Message { content: "B" },
        ]
    };
    for msg in messages.iter_mut() {
        *msg = Message { content: "C" }
    }

    // error[E0502]: cannot borrow `messages` as immutable because it is also borrowed as mutable
    println!("Tasks: {:?}", messages);
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744806418a4594801.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信