Widespread Swift Protocol for Extensions

182 Views Asked by At

Widespread Protocol for Extensions

Swift 4.1, Xcode 9.3

I was wondering, what are some of the most overarching protocols in Swift. I want to make an extension that applies to values that can be set. The purpose of this was to make it easier to write more one-lined code.


My Extension:

Note: for the time being, the "overarching" protocol that I am extending is Equatable.

extension Equatable {
    @discardableResult public func set(to variable: inout Self) -> Self {
        variable = self
        return self
    }
}

Caveat: I would like to be able to use .set(to: ) for values that don't conform to Equatable as well.


Usage:

let ten = 10
var twenty = 0

(ten + 10).set(to: &twenty)

print(twenty)
// Prints "20"

This can be helpful when you need to set and return a value, now only one line of code is required to do so.

return value.set(to: &variable)

Final Question

How do I make .set(to: ) more far reaching, without needing multiple instances of it?

  • For example, if I wrote the same extension for Equatable, CustomStringConvertible, CVarArg, there would be multiple suggestions of the same extensions for many values that conform to all 3 of these protocols.
  • If this is not possible, what is the best overarching protocol that I can use?

Bonus Question: is there a way in an extension to do something not dissimilar to extension Equatable where !(Element: CustomStringConvertible) or extension Equatable where !(Element == Int) (use the where predicate for exclusion purposes)?


1

There are 1 best solutions below

2
On BEST ANSWER

In most cases, I strong discourage this kind of code. "One-line" code is not generally a goal of Swift. Clear and concise is a goal, with clear winning when they're in conflict. Extending Any this way (even if it were legal) is generally a very bad idea since set(to:) could easily collide.

But in limited circumstances this may be useful within a single file or for a special use. In that case, it's easily implemented with operators.

infix operator -->
private func --> <T>(lhs: T, rhs: inout T) -> T {
    rhs = lhs
    return lhs
}

let ten = 10
var twenty = 0

(ten + 10) --> twenty

print(twenty)
// Prints "20"

The more natural way to do what you're describing is with protocols that you explicitly conform. For example:

protocol Settable {}

extension Settable {
    @discardableResult public func set(to variable: inout Self) -> Self {
        variable = self
        return self
    }
}

extension Int: Settable {}
extension String: Settable {}
extension Array: Settable {}
extension Optional: Settable {}

You can attach Settable to any types that are useful for this purpose, and these extensions can be provided anywhere in the project (even in other modules). There is no way to attach a method to every possible type in Swift.