Is parametric mutability working? Or am I misunderstanding what it is? This does not let me mutate y.
@value
struct MyThing:
var thing: String
fn by_ref[
is_mutable: Bool, //, origin: Origin[is_mutable]
](ref [origin]y: MyThing):
"""A function that takes a reference that is parametric over mutability."""
@parameter
if is_mutable:
y.thing = String("Can overwrite it")
print(y.thing)
Error
error: expression must be mutable in assignment
y.thing = String("Can overwrite it")
But this works as expected:
fn take_mut(mut y: MyThing):
"""A function that takes an mutable reference of a MyThing."""
y.thing = "Overwritten!"
# y = MyThing("Overwritten twice!") # also works
print(y.thing)
And the ref docs say From inside the called function, a ref argument looks like a read or mut argument.
Nope, not yet i think,
it is a sharp edge that can be solved with rebind or mut_cast
(refine the parameter)
I think it was on the sharp edges page at some point but not sure
This function is meant to be used in uncommon cases where a parametric type depends on the value of a constrained parameter in order to manually refine the type with the constrained parameter value.
`
Another different solution is to create two function overloads:
one for mutable and one for not mutable
Thanks for saving my sanity! Overloading the mutability is very slick. I’ll give that a try.
I reread the sharp edges doc and don’t see anything about this there. It’s odd that the docs for ref demonstrate it, but don’t actually do any mutation, which made me question whether I really understood what was going on.
If any Modular people come by, is this a known thing on a roadmap for fixing? This is a pretty awesome feature coming from Rust.
I’ve been looking through the standard library for an example of using rebind to change the mutability, but haven’t found one. Have you see this in action somewhere I can look at?
This was the best I was able to come up with so far:
fn take_ref[
is_mutable: Bool, //, origin: Origin[is_mutable]
](y: Pointer[MyThing, origin]):
"""A function that takes a reference that is parametric over mutability."""
@parameter
if is_mutable:
var y_mut = rebind[
Pointer[MyThing, MutableOrigin.cast_from[origin].result]
](y)
y_mut[].thing = String("Can overwrite it")
print(y[].thing)
Trying to overload with a take(mut y: MyThing) and take(read y: MyThing) result in the call site giving an error saying I need to cast, but I have no idea how to cast appropriately there.
Likewise, I can’t figure out how to rebind ref[origin] MyThing because rebind can’t include a ref keyword.
This is behaving as designed. The use of rebind is necessary when you have additional information known about a type that isn’t obvious from the code.
In this case, mojo won’t let you mutate something with a parametric mutability, because it isn’t valid for all instantiations of the code, e.g., this would be invalid:
Mojo wants to make sure - at definition time - that take_ref can work with all instantiations. We try hard to do this because this is good for QoI. In your case, you’re checking for additional information with parameter if: this is cool, but mojo doesn’t automatically refine the type inside the body of that. As such, the use of rebind (like you’re doing) is the right way to go. thanks!