Question on behavior of `ref` argument

I have a question regarding the behavior of the reg argument (when the keyword reg is used as the argument convention). The question arises from the following code that won’t compile in Mojo v25.4.

def changit(ref a: Int):
    a = 10


def main():
    var a = 1
    changit(a)

The error message is:

error: expression must be mutable in assignment
    a = 10
    ^

This suggests that the ref keyword is equivalent to the read keyword in this case.


However, I also noticed the following facts:

  1. ref can be used to let the argument carry parametric mutability. So that you can do something like printing the mutability of the origin, e.g.,
    def is_mutable[
        mutability: Bool, //, origin: Origin[mutability]
    ](ref [origin]x: Int):
        print("The value is mutable?", mutability)
    
    def main():
        var a = 10
        is_mutable(a)
    
  2. The reference created by the ref keyword in a local scope can be mutable. For example,
    def main():
        var a = 1
        ref b = a
        b = 10
    
  3. The ref keyword is used in a loop to make that the element mutable. For example,
    def main():
        var lst = [1, 2, 3]
        for ref i in lst:
            i += 1
    

Then, a question comes into my mind: why is the argument a in the function def changit(ref a: Int) immutable?

I have several guesses:

  1. This feature will change in future, so that ref argument can be mutable in case the origin is mutable. In other words, ref will either work as read or mut.
  2. This is the desired behavior of ref. It just contains mutability information of the origin, but is not mutable in the scope, which means that it behaviors as read.

Can anyone help me verify my propositions? Thank you very much in advance!

ref means “Caller determined mutability”. In this case, the caller can call with an immutable ref and you try to mutate that, so the compiler rejects that case. It isn’t erroring for the mut case because that code is fine in the mut case.

In C++, you could rely on how templating works to have this case never show up. In Mojo, the type system requires that your code works for all possible input cases, so it’s providing a case where your code isn’t valid.

For the most part, ref is useful when you want to retrieve something from a collection or perform a similar action where you don’t actually care about the mutability of self, since you’re just handing the thing upwards. For most code that isn’t doing that, you’ll want to explicitly specify read or mut.

1 Like

Hi @owenhilyard , thank you for your detailed answer! I understand it now. In this sense, I should use mut explicitly in case I want to change a value, because the mutability of the argument cannot be inferred from the ref keyword or the parametric mutability of the origin.

1 Like

Yes, if you want to always get a mutable reference, use mut.

3 Likes