Hi Nelson —
Ah, I understand your question better now. The two refs serve two different purposes, but because the things they’re referring to are syntactically subtle/absent, it can be a bit confusing.
The ref that comes after the argument list says that the result being returned by the procedure should be returned by reference, permitting the caller to modify it. It is this ref that permits the assignment to r.lft(2) in your code to modify r.u directly, whereas r.rgt() would not support similar assignments to it since it uses the default const (copy-out) intent for integers.
If the declaration of lft was expanded to include the return type, it would look like:
proc ref lft(in i: int) ref : int {
which is intended to mean “we will be returning an integer, and it should be returned by ref.” The syntax in this form is meant to mimic that of a standalone ref declaration, like:
ref xr: int = myInt; // make 'xr' refer to 'myInt'
but without introducing a new identifier like xr since, in Chapel, the return statement doesn’t have an identifier associated with it. In your case, you’ve relied on type inference to infer the : int return type, but the ref is required since ints are returned by const (copy-out) by default (which again is why r.rgt(2) couldn’t be assigned to similarly).
On the other hand, the ref that precedes the argument list says how the this object receiver upon which the method is being called should be “passed” (or made available) to the method. In your case, the this is the record r upon which you’re calling these methods. By default for methods, this is passed by const, meaning that it can’t be modified. But since lft() returns one of the record’s fields by ref, permitting it to be modified, it’s important that this itself can be modified, since a record’s identity is the values of its fields, and if they’re modified, so is the record. This is the reason that the ref on the left-hand side is needed.
As mentioned in my previous response, this is a recent change, as this used to default to ref for records until fairly recently. And that’s also why this is merely a warning rather than an error (like “method that returns field values by ref must have a ref this intent”).
Syntactically, the placement of this ref is positioned where the this receiver would be placed in a call, which is to say, right before the argument list. Like the return value, since the this identifier doesn’t show up explicitly in the method definition, it seems to be floating there, syntactically. When using the secondary method declaration syntax to define a method outside of a record, things are slightly clearer, since the ref appears adjacent to the tr type that represents the this receiver for the method:
proc ref tr.lft2(in i: int) ref { … }
Note that in these examples, I’m not trying to suggest that secondary method declarations or explicit return types are inherently better—that’s a personal style choice. I’m just using them to demonstrate how the refs interact with other nearby syntactic elements.
Hope this helps clarify things, but definitely let us know if it doesn’t!
-Brad