Rust 2030 Christmas list: Inout methods

Big asterisk on this: they don't, not on the abstract machine, at least. On the abstract machine, there's shadow state (that isn't represented on the physical machine) which is handled differently for shared or unique references: the retag/reborrow operation — in Stacked Borrows (Miri) this is what manages the borrow stack and puts the Shr/Uniq tags into the borrow stack. At an abstract machine level, it truly is two different functions, which may trivially reduce down to the same concrete machine code and be deduplicated.

Lifetimes, however, actually are a purely frontend construct; the abstract machine only cares that references are operationally used in a valid way.

2 Likes

This post gave me the idea that maybe it could be spelled ~mut instead of inout? Instead of C: constness, we have M: mutability which applies to all ~mut uses within the function/struct/etc.

I wonder if this would allow one to write Iter and IterMut impls with a single syntax in some way?

Why don't you separate the finding from the getting, i.e. return some index/search result type from find_child and then implement both Index and IndexMut for that type?

Similar to how find-Methods on arrays usually return an index and not a reference to the element.

If we're being generic over mutability, maybe we should be generic over ownership as well? :v

impl<'a, T, const N: usize> Iterator for Iter<&'a Node<T, N>> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        let Self(stack) = self;
        stack.pop_front().map(|Node { ch, value }| {
            stack.extend(ch.iter().flat_map(|o| Some(&**o.as_ref()?)));
            value
        })
    }
}
impl<'a, T, const N: usize> Iterator for Iter<&'a mut Node<T, N>> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        let Self(stack) = self;
        stack.pop_front().map(|Node { ch, value }| {
            stack.extend(ch.iter_mut().flat_map(|o| Some(&mut **o.as_mut()?)));
            value
        })
    }
}
impl<T, const N: usize> Iterator for Iter<Node<T, N>> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        let Self(stack) = self;
        stack.pop_front().map(|Node { ch, value }| {
            stack.extend(ch.into_iter().flat_map(|o| Some(*o?)));
            value
        })
    }
}

Look at all this duplication! Not to mention the corresponding

impl<'a, T, const N: usize> IntoIterator for &'a Node<T, N> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = Iter<Self>;
    fn into_iter(self) -> Self::IntoIter {
        Iter(VecDeque::from([self]))
    }
}
impl<'a, T, const N: usize> IntoIterator for &'a mut Node<T, N> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = Iter<Self>;
    fn into_iter(self) -> Self::IntoIter {
        Iter(VecDeque::from([self]))
    }
}
impl<T, const N: usize> IntoIterator for Node<T, N> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = Iter<Self>;
    fn into_iter(self) -> Self::IntoIter {
        Iter(VecDeque::from([self]))
    }
}

(This is not a serious proposal. In my experience these cases are annoying, but rare)

IME they come up quite often in the builder pattern. I always end up writing methods like...

impl Thing {
    fn with_foo(mut self, foo: Foo) -> Self {
        self.foo = foo;
        self
    }

    fn set_foo(&mut self, foo: Foo) -> &mut Self {
        self.foo = foo;
        self
    }
}
1 Like

Quick note: in the library I'm currently working on (fork of druid called widget-cruncher), I'm realizing that mutability effects would be less useful than I thought.

That's because, although my code still does a lot of tree visiting and stuff, the code that visits with shared references and the code with mutable references end up being very different, mostly because mutating the widget tree requires propagating changes.

I don't think that means an inout syntax would be useless, but at the very least it wouldn't be useful for my current use-case. I felt it was important to mention that.

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.