问题
This question is a follow up to my earlier question: I expected the system to report non protocol conformance, but it does not! Why?
Please read the referred question for you to get a better idea of the constraints at hand.
I created a generic function in Swift that will reject its parameter unless such a parameter is Optional
. The function I created fully works and does what I desire.
Meaning, any calls to onlyCallableByAnOptable(...)
, even inside an if let
, will yield error due to non-protocol conformance, exactly as desired.
Errors like: Argument type 'UIColor' does not conform to expected type 'Optable'
My only question is: Is there a simpler solution?
To make it clear:
func onlyCallableWithAnOptinalParameter<T>(:T)->T
needs to work when called in anif let
statement likefunc test()
does.
protocol Optable {
associatedtype OptableType
func optionalOptable() -> OptableType?
}
func onlyCallableByAnOptable<T>( _ value: T) -> T.OptableType? where T: Optable {
return value.optionalOptable()
}
extension Optional: Optable {
typealias OptableType = Wrapped //: Wrapped is the type of the element, as defined in Optional
func optionalOptable() -> OptableType? {
return self
}
}
class TestOptable {
static func test()
{
let c = UIColor.blue
let s = "hi"
let i = Int(10)
let oi: Int? = 10
if let v = onlyCallableByAnOptable(c) { // ERROR, as was desired.
print("color \(v)")
}
if let v = onlyCallableByAnOptable(s) { // ERROR, as was desired.
print("string \(v)")
}
if let v = onlyCallableByAnOptable(i) { // ERROR, as was desired.
print("integer \(v)")
}
if let v = onlyCallableByAnOptable(oi) { // OK, as expected.
print("optional integer \(v)")
}
}
}
回答1:
You might want to give this protocol a better name, but I don't foresee any problems with it as-is, unless you're making your own ExpressibleByNilLiteral
types that don't wrap.
protocol ExpressibleByNilLiteral: Swift.ExpressibleByNilLiteral {
associatedtype Wrapped
}
extension Optional: ExpressibleByNilLiteral { }
func onlyCallableByAnOptional<Optional: ExpressibleByNilLiteral>(_ optional: Optional) -> Optional.Wrapped? {
optional as? Optional.Wrapped
}
Recommendation: use an initializer. (Downside is the argument label being necessary to disambiguate, but I personally like the explicitness because of how weird this case is. i.e. Swift makes it easy to enforce that something is not optional, but not vice versa.)
extension Optional: ExpressibleByNilLiteral {
init<Optional: ExpressibleByNilLiteral>(optional: Optional) where Optional.Wrapped == Wrapped {
self = optional as? Wrapped
}
}
+
if let v = Optional(optional: i) { // ERROR, as was desired.
print("integer \(v)")
}
if let v = Optional(optional: oi) { // OK, as expected.
print("optional integer \(v)")
}
回答2:
You need to make the value
parameter you pass into onlyCallableByAnOptional
optional. Likewise, on the return statement of that function, you will also need to optionally unwrap value
so that it can execute the optionalOptable
function.
func onlyCallableByAnOptable<T>( _ value: T?) -> T.OptableType? where T: Optable {
return value?.optionalOptable()
}
回答3:
The following is @Jessy's solution, which is basically a simplification of my solution.
Well done Jessy.
I decided to rewrite it here with different/simpler, hopefully, less "confusing" names for the generic type and the protocol, to make it more readable, let prone to confusion by newbies, and also more similar to the names used in my question.
If anyone happens to know of an even more elegant approach, you are very welcome to post it.
protocol Optable {
associatedtype Wrapped
}
extension Optional: Optable { }
func onlyCallableByAnOptable<T>(_ value: T) -> T.Wrapped? where T: Optable {
return value as? T.Wrapped
}
OR, if you happen to prefer Jessy's solution which uses an initializer, here is a renamed version:
protocol Optable {
associatedtype Wrapped
}
extension Optional: Optable {
init<T: Optable>(optional o: T) where T.Wrapped == Wrapped {
self = o as? Wrapped
}
}
来源:https://stackoverflow.com/questions/60550468/how-to-create-a-generic-function-in-swift-that-will-reject-the-given-parameter-u