Isn't that true of ADTs in all languages? I can't think of a single language with ADTs that lets you change the tag/variant of an existing value.
#[derive(Debug)]
pub enum Example {
Foo(i32),
Bar(&'static str),
}
let mut ex: Example = Example::Foo(42);
println!("{ex:?}"); // Foo(42)
let ex_ref: &mut Example = &mut ex;
*ex_ref = Example::Bar("hello");
println!("{ex:?}"); // Bar("hello")
Given a mutable reference to a value of enum type, you can replace it with another variant. Or you can swap it out with any other value of the same type, even if the variants are different. This is most commonly used for Option<T>, where you can insert or remove the contained value as long as you have a reference.The limitation here is that for as long as the mutable reference is live, no other code can access the value. So when you do change the variant, you can't have any other references sitting around that point to the removed subfields.
[0] https://play.rust-lang.org/?version=stable&mode=debug&editio...
You can accomplish the same thing in C and C++ because they also have in-place value semantics and allow you to take the address of any variable. You can't do that in Java only because Java doesn't let you take references to variables.
'Values' in Rust have no identity, except for their address. They're just a bunch of bytes in a row. What could it mean for a value to exist, except for it to be present at a set place in memory? If I have an instance of a Java class, and I change all the fields, I'd hardly say the instance has been replaced with a new instance.
If you insist, I'd say "Java's sealed classes are more limited in that when you have a bunch of references to the same thing, you can change the values in the fields of that thing (and have it be reflected in other references), but you can't change which variant it is." Call that thing a 'value' or a 'variable', it doesn't change the visible outcome compared to enums in Rust, or discriminated unions in C/C++.
Pointers and value semantics are nice, but have nothing to do with ADTs. For example, in Rust, you can also do:
let mut ex: i32 = 42;
println!("{ex:?}"); // 42
let ex_ref: &mut i32 = &mut ex;
*ex_ref = 53;
println!("{ex:?}"); // 53
And in C: int x = 42;
printf("%d\n", x); // 42
int* x_ref = &x;
*x_ref = 53;
printf("%d\n", x); // 53
In these examples, we haven't mutated the number 42 into the number 53. We've simply stored an entirely new value in the location of `x`. In your Rust example, you're doing the exact same thing with an ADT. The variant case isn't being changed. You're creating a new value and storing it in an existing storage location. Every pointer pointing to that storage location sees the update because they all point to the same storage.