Reputation: 3341
Is it possible to give an optional generic parameter a default value?
I'm trying to do something like this:
func addChannel<T>(name: String, data: T? = nil) -> Channel {
}
let myChannel = addChannel("myChannelName")
But I'm getting an error saying
Argument for generic parameter 'T' could not be inferred
Is it just a case of what I'm trying to do being impossible?
Upvotes: 29
Views: 18462
Reputation: 19347
This synthesises a bunch of the other answers here — specifically, I wanted to use a default, while still having a constraint on the type, without adding overloads.
Specifically, my example is passing an optional request body (of generic, Encodable type), and returning a response body (of generic, Decodable type). The result type must be specified.
static func doRequest<Payload: Encodable, Result: Decodable>(
_ resultType: Result.Type,
path: String,
body: Payload? = Optional<Bool>.none,
method: String = "GET",
) async throws -> Result {
// ... calculate url ...
var request = URLRequest(url: url)
request.httpMethod = method
if let body {
request.httpBody = try JSONEncoder().encode(body)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
// ... perform request, handle errors ...
return try JSONDecoder().decode(Result.self, from: data)
}
The key is specifying Optional<Bool>.none
as the default. Actually, it doesn't have to be Bool
specifically, as long as it's Encodable
. (This rules out Optional<Any>.none
, but I didn't feel like making up a "DefaultEncodable"
just for this.) It doesn't matter, since we don't specify such a value, and we'll only hit the if let body { … }
interior if we actually have a proper body to encode anyway.
Upvotes: 0
Reputation: 129
I encountered a similar issue while defining a complex function with a large number of generics, for which overloading would have resulted in an unmanageable number of variations. Here is the code:
struct Channel{
//Your code here
}
class ChannelManager{
static let typedNil : Int? = nil //<--- This is what eliminates the error "Argument for generic parameter 'T' could not be inferred"
public static func addChannel<T>(name: String, data: T? = typedNil) -> Channel{
//Your code here
return Channel() //To demonstrate that code compiles
}
}
By declaring typedNil
in this manner and using it as a default parameter, the default still is nil
as expected, but the compiler sees it as a nil
int
and so is able to infer the type, avoiding the requirement of adding additional protocol conformance to existing types, or requiring overloads. Note that declaring typedNil
as static
, otherwise compilation will fail with the error "Cannot use instance member 'typedNil' as a default parameter."
Upvotes: 1
Reputation: 43066
I use a combination of the existing answers so that I can both call the function without the optional argument and avoid implementing the function twice.
func addChannel(name: String) -> Channel {
addChannel(name: name, data: Optional<Any>.nil)
}
func addChannel<T>(name: String, data: T? = nil) -> Channel {
...
}
As mentioned above you should avoid Any
. In my case I know that T
is an Encodable
so I use this:
struct DefaultEncodable: Encodable {}
func function1(name: String) {
addChannel(name: name, data: Optional<DefaultEncodable>.nil)
}
func function1<T: Encodable>(name: String, data: T? = nil) {
...
}
Upvotes: 2
Reputation: 1070
I ran into the same problem. While not being able to use the generic default traditionally — leaving the argument out completely — I prefer the below to implementing overloads:
let myChannel=addChannel("myChannelName", data: Optional<Any>.none)
Upvotes: 4
Reputation: 299275
It's impossible in the way you've done it. Given just the code above, what type is T
? The compiler, as it says, can't figure it out (neither can I, and I assume you couldn't either because the data's not there).
The solution to the specific question is to overload rather than use defaults:
func addChannel<T>(name: String, data: T?) -> Channel { ... }
func addChannel(name: String) -> Channel { ... }
let myChannel = addChannel("myChannelName")
But it raises the question of what you're doing here. You would think that Channel
should be Channel<T>
. Otherwise, what are you doing with data
? Without resorting to Any
(which you should strongly avoid), it's hard to see how your function can do anything but ignore data
.
With Channel<T>
you can just use a default, but you'd have to provide the type:
func addChannel<T>(name: String, data: T? = nil) -> Channel<T> { ... }
let myChannel: Channel<Int> = addChannel("myChannelName")
Otherwise the compiler wouldn't know what kind of channel you're making.
(UPDATE ~ Swift 5.2)
Sometimes you'd like a default type for T
. You can do that with an overload. For example, you might want the default type to be Never
. In that case, you would add an overload like this:
func addChannel<T>(name: String, data: T? = nil) -> Channel<T> { ... }
func addChannel(name: String) -> Channel<Never> {
addChannel(name: name, data: Optional<Never>.none)
}
With that, you can have a simpler call:
let myChannel = addChannel(name: "myChannelName") // Channel<Never>
Upvotes: 44