I'm not a go expert, but imo the main difference is ergonomics and clarity. Rust/Swift style ADT's plus pattern matching gives you a very concise and readable way to declare and use sum-types, and the Go way seems to have more boilerplate.
Also with Rust for instance you have more robust pattern matching. So you don't have to match only on type, you can match on complex criteria (i.e. foo is a Circle, with foo.radius < 10).
I find this type of programming very expressive and easy to reason about.
Yeah exactly, imo the way the Go version requires you to declare an interface, in this case with a no-op method essentially acting as a tag, and declare conformance on your types is quite noisy and feels like a hack.
Also I prefer with Rust-style sum types that the declaration happens in one place. In the Go version your Shape types could be scattered around all over the place, which makes it harder to parse and understand if you're reading unfamiliar code.
This Go code is wildly non-idiomatic. Type assertions via `.(type)` are a tool of last resort, not something that an application developer should turn to as a solution to a problem, and certainly not post-1.18, which permits generics.
There is no way to express an enum whose possible values are of different types.
In Go code will typically "offer" concrete types, and "accept" abstract interfaces. Abstractions over concrete types are expressed by consumers who want to operate on those types, rather than producers who provide implementations of them. So your Shape would probably end up an interface defined by whatever consuming code wanted to treat Circles and Rectangles equivalently.
Also with Rust for instance you have more robust pattern matching. So you don't have to match only on type, you can match on complex criteria (i.e. foo is a Circle, with foo.radius < 10).
I find this type of programming very expressive and easy to reason about.