Consider the following code:
import ballerina/io;
type User record {|
string name;
string email;
|};
const USER = {
name: "John Doe",
email: "[email protected]"
};
public function main() {
User user = USER.clone();
user.name = "Jane Doe";
io:println("User name: " + user.name);
}
Is this code correct? I have a constant map and clone it as user and updated one field. Since it is a clone with a constant map, are the fields cloned as readonly? Then the clone() should return a compiler error, since in the target type, the field is not explicitly readonly right?
Currently this is failing with this runtime error:
error: {ballerina/lang.map}InherentTypeViolation {"message":"cannot update 'readonly' field 'name' in record of type '(service:record {| "John Doe" name; "[email protected]" email; |} & readonly)'"}
But if I use cloneWithType() it is working fine:
public function main() returns error? {
User user = check USER.cloneWithType();
user.name = "Jane Doe";
io:println("User name: " + user.name);
}
Can someone explain the behaviour here?
In here the type of the USER is inferred as
by the Ballerina compiler.
The USER.clone() will return the
USERrecord itself since it is immutable.So, the type of USER is a subtype of
Userrecord. So Compiler allows to assign that into the user variable. But the original type of theUSERrecord is readonly. So we cannot update the fields of the record(We can't update fields in readonly maps). That is the meaning of the error message.Example:- Assume there is a type called A.
In here the type of the b is A & readonly So we cannot update the fields in value b
But if you use CloneWithType function, It will clone the value and convert it into the expected type.
So in here the type of the user is User.
So we can update the fields in here.
By the way important to know that when you use clone() (or cloneReadOnly()) with an immutable value a copy will not be created. The value itself will be the result.