Hi @michael2024, welcome to the Swift Forums!
BaseShape doesn't help you here because its single stored property:
private var wrappedShape:Shape
means the same thing as:
private var wrappedShape:any Shape // <--
So you still have an existential (any) type, just wrapped in a struct. That's not a solution to your original problem.
Your creat…Shape function is essentially a "factory" function — a concept that exists in other languages but doesn't exist as a language feature in Swift. There are two Swift ways of doing this, both of which are not completely satisfactory:
1. Return an existential from creatShape:
func creatShape(_ seed:Int) -> any Shape { }
In that case, you can "open" the existential when using the resulting value:
func useShape(_ shape: some Shape) { ]
let newShape = creatShape(0)
useShape(newShape)
Inside the useShape function shape is concrete, not existential.
2. Make creatShape generic:
func creatShape<T: Shape>(_ seed:Int) -> some T { }
In this case, when you create a Shape, you can get a concrete-but-opaque type:
func useShape(_ shape: some Shape) { ]
let newShape = creatShape(0)
useShape(newShape)
In this case, the compiler knows the type of newShape, but your code doesn't. You can still call a function like useShape, but there's no existential "overhead" to newShape once it's created.
What's interesting here is the anti-symmetry of opaque types described by some …. On a function parameter, it makes the parameter type opaque, opening an existential type from the calling site if necessary. On a function return, it makes the return type opaque, avoiding what would otherwise need to be an existential type.
You also have a third option here:
3. Don't use a factory function.
This isn't a solution to your actual problem, but in many cases in Swift it's useful to see if you can come up with a different approach that doesn't need a single factory function that returns values of all your related types.