Motivation
Explicit self
on member access is required to resolve symbol shadowing.
Overloading self
with capture semantics leads to confusion:
Inconsistent
class Style1 {
init() {
some = {
bar()
}
}
var some: Any?
func bar() {}
}
Call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit
class Style2 {
init() {
some = bar
}
var some: Any?
func bar() {}
}
(no warning) SR-8536
class Style3 {
init() {
func niente() {
bar()
}
some = niente
}
var some: Any?
func bar() {}
}
(no warning)
Confusing
Explicit self.
doesn't indicate that self
will be captured:
class Style5 {
init() {
some = self.bar
some = self.nada
}
var some: Any?
var nada: Int = 42
func bar() {}
}
class Style6 {
init() {
foo(self.nada)
bar(self.nada)
}
var some: Any?
var nada: Int = 42
func foo(_ cb: @autoclosure () -> Int) {}
func bar(_ cb: @escaping @autoclosure () -> Int) { some = cb }
}
Problems
A team adopting explicit self
on every member access is left with no indicators which self
is captured.
This makes explicit self
a conditional requirement – it requests you not to write self
on all other code.
Learners of the language are confused: explicit self
or not, it looks just a member access to me.
Not every example is currently covered in the compiler.
Can we do better?
Explicit capture should have an explicit syntax.
Closures
Closures have capture lists, and this is the natural place for the explicit self
:
class Style1b {
init() {
some = { [self] in
bar()
}
}
var some: Any?
func bar() {}
}
Writing bar()
or self.bar()
is optional, and is left to teams' preference.
Method reference
One way to resolve this with existing syntax is to require wrapping method access in a closure, see above.
Otherwise, discuss an explicit syntax, like:
class Style2b {
init() {
some = [self] bar
}
var some: Any?
func bar() {}
}
Nested function
Can compiler enforce the same escaping/non escaping rules as on closures?
Should we have an explicit capture for nested functions, or compiler should force you to use a closure?
One possible explicit syntax:
class Style3b {
init() {
func niente() [self] {
bar()
}
some = niente
}
var some: Any?
func bar() {}
}
class Style3c {
init() {
func niente() [self] {
nada = 11
}
foo(niente)
bar(niente)
}
var some: Any?
var nada: Int = 42
func foo(_ cb: () -> Void) {}
func bar(_ cb: @escaping () -> Void) { some = cb }
}
Auto-closure
With existing syntax, compiler could require wrapping member access in a closure, see above.
Otherwise, discuss an explicit syntax, like:
class Style6b {
init() {
foo(nada)
bar([self] nada)
}
var some: Any?
var nada: Int = 42
func foo(_ cb: @autoclosure () -> Int) {}
func bar(_ cb: @escaping @autoclosure () -> Int) { some = cb }
}
Explicit capture all
A follow-up discussion: should the language require explicit capture of all objects?
class Style1x {
init() {
let x = X()
some = { [self, x] in
bar()
x.foo()
}
}
var some: Any?
func bar() {}
}
Discussions
- Proposal: Re-instate mandatory self for accessing instance properties and functions (David Hart)
- An upcoming proposal for simplifying leak free, safe closures.
- [Draft] Guarded closures and `@guarded` arguments
- Making capturing semantics of local functions explicit
- [Pitch] Improving capturing semantics of local functions
- Explicit capture semantics applied too broadly?
- Revisiting requiring explicit `self.` when passing a method as an escaping closure
- Request explicit capture of self instead of defaulting to a strong capture of self
- Implicit retain cycle
- [Style] Explicit self when referencing instance member