// it compiles without any problems, regardless ModifiedContent is not a View
func modify(with viewModifier: some ViewModifier) -> some View {
let content: ModifiedContent<Self, some ViewModifier> = modifier(viewModifier)
return content
}
but if we write same in generic way, we got expected error message
func modify<V>(with viewModifier: some ViewModifier) -> V where V : View {
let content: ModifiedContent<Self, some ViewModifier> = modifier(viewModifier)
return content // error: Cannot convert return expression of type
// 'ModifiedContent<Self, some ViewModifier>' to return type 'V'
}
CodePudding user response:
Here's your first, working declaration:
func modify(with viewModifier: some ViewModifier) -> some View
You used the some
keyword twice in that function declaration. It is important to understand that some
means different things in those two uses.
In the first use (viewModifier: some ViewModifier
), the some
keyword is “syntactic sugar” for a implicit, unnamed generic parameter. We can de-sugar the use of some
there by replacing it with an explicit, named generic parameter, like this:
// Replace `some` in parameter position with an explicit generic parameter:
func modify<VM: ViewModifier>(with viewModifier: VM) -> some View
When you call that modify
method, you as the caller choose a real type to replace the VM
parameter. So you can call modify
many times, with a different type to replace VM
each time. The only constraint is the replacement type must always conform to the ViewModifier
protocol.
The second use of some
in your first function declaration (in -> some View
) has a different meaning. It means the function will return a value whose type conforms to View
, but the specific type is chosen by the body of the function. You as the function caller do not choose the type.
To summarize the differences:
The
some
keyword in parameter position- represents an implicit, unnamed generic type parameter;
- can be de-sugared to an explicit, named generic parameter without changing the function body;
- is replaced by a type chosen by the caller of the function.
The
some
keyword in return type position- represents a type known to conform to a protocol;
- is a type chosen by the body of the function.
Now let's look at your second, broken function declaration:
func modify<V>(with viewModifier: some ViewModifier) -> V where V : View
This is like your first function declaration, except that you have replaced the some
in return type position with the generic parameter V
.
The problem is that these don't mean the same things. As I explained above, a return type of some View
means the function body chooses the actual type, but a generic parameter is always chosen by the caller of the function.
That's why you get the error when your function tries to return content
. There's no guarantee that content
has type V
. Your function knows so little about type V
(you only know it conforms to View
) that there's no way for the function to even create a value of type V
(because the View
protocol doesn't have any init
requirements).
You also have this comment on your first, working function declaration:
// it compiles without any problems, regardless ModifiedContent is not a View
I assume you declared the modify(withViewModifier:)
method in an extension View
. That means Self
conforms to View
, so ModifiedContent<Self, some ViewModifier>
conforms to View
.
CodePudding user response:
ModifiedContent
is a generic View so you can return it as opaque result type some View
.
when returning -> V where V : View
ModifiedContent
has to match any V
type where it is called from, which is not possible.
see as well: https://forums.swift.org/t/how-to-use-a-generic-type-as-the-functions-return-type/37666/2