Remix.run Logo
pkulak 2 days ago

This is why I love how Rust approached this; almost by accident to make borrow checking work. Every reference is either mutable or not, and (with safe code), you can't use an immutable reference to get a mutable reference anywhere down the chain. So you can slowly construct a map through a mutable reference, but then return it out of a function as immutable, and that's the end of it. It's no longer ever mutable, and no key or value is either. There's no need to make a whole new object called FrozenHashMap, and then FrozenList, and FrozenSet, etc. You don't need a StringBuilder because String is mutable, unless you don't want it to be. It's all just part of the language.

Kotlin _kinda_ does this as well, but if you have a reference to an immutable map in Kotlin, you are still free to mutate the values (and even keys!) as much as you like.

rcxdude 2 days ago | parent | next [-]

Only if you're returning a reference or wrapping it in something that will only ever return a reference. If you return an object by value ('owned'), then you can do what you like with it and 'mut' is just an light guardrail on that particular name for it.

vlovich123 2 days ago | parent | prev | next [-]

You cannot return an immutable version. You can return it owned (in which case you can assign/reassign it to a mut variable at any point) or you can take a mut reference and return an immutable reference - but whoever is the owner can almost always access it mutably.

pkulak 2 days ago | parent | next [-]

Arg, you’re right. Not sure what I was thinking there. I still think my point stands, because you get the benefits of immutability, but yeah, I didn’t explain it well.

tekne 2 days ago | parent | prev [-]

I mean, if you return an immutable reference, the owner in fact cannot mutate it until that reference is dropped.

If you in fact return e.g. an Rc::new(thing) or Arc::new(thing), that's forever (though of course you can unwrap the last reference!)

aw1621107 a day ago | parent [-]

> I mean, if you return an immutable reference, the owner in fact cannot mutate it until that reference is dropped.

Might be worth noting that "dropped" in this context doesn't necessarily correspond to the reference going out of scope:

    fn get_first(v: &Vec<i32>) -> &i32 {
        &v[0]
    }

    fn main() {
        let mut v = vec![0, 1, 2];
        let first = get_first(&v);
        print!("{}", first});
        v.push(3); // Works!
        // print!("{}", first); // Doesn't work
    }
the__alchemist 2 days ago | parent | prev [-]

My favorite part of rust is its explicit control over mutability, in the manner you describe.