Compiler method selection issue

If there are two methods with similar arguments and one with a return value, why does the compiler complain it doesn't know which method to use when no result is expected (e.g., clear(1..3))? Isn't the lack of an assignment enough clue to discern which method to use? Is there a way to make this work?

  public mutating func clear(range: IntRange) {}
  public func clear(range: IntRange) -> Self {}

You can assign the result of a function that doesn't return anything (in other words returning Void) to a variable, and you can leave the result of a function that returns something non void unassigned (the latter would trigger a warning unless the function is marked with @discardableResult), so from a point of view of a (pedantic) compiler there's an ambiguity here, unless you are calling it on a let constant in which case the second call will be chosen unambiguously.

Swift API naming guidelines would have you name the non-mutating method clearing(range:), by the so-called “ed/ing” rule. That is the intended solution.

Overloading by return type is supported but discouraged, and using the same name for both mutating and non-mutating methods is discouraged.

6 Likes

Thanks, I'll adopt the Swift "ed/ing" rule.

Strange, then, that the compiler should be confused about which method to invoke.

That doesn't seem like the "right thing to do".

It's mostly allowed so that generic code doesn't break when a generic type parameter happens to resolve to Void. It also makes the language a little more regular.

2 Likes

Good reasons.

Also could help in a situation like this:

struct S {
    func foo() {}
}
var s: S? = ...
let v = s?.foo()
print("v: \(v)")
3 Likes
  1. Explicit typing:
struct S {
  mutating func clear() { }
  func clear() -> Self { self }
}

var s = S()
s.clear() as Void
s.clear() as S
  1. Referring to the method as a closure, which can only be the nonmutating variant.
(s.clear)()
2 Likes