Is it just me or did we end up having partly `StaticSelf` in Swift?

Just out of curiosity I tried to create a protocol which would leak Self through a type alias in Swift 5 (without SE-0068). The result was surprising.

The compiler can happily make any type conform to the protocol. This allows to refer to the current type with a type alias called StaticSelf.

protocol Selfy {
  typealias StaticSelf = Self
}

extension NSObject: Selfy {}

extension UIView {
  var _view: StaticSelf {
    return self
  }
}

extension UIButton {
  var _button: StaticSelf {
    return self
  }
}

struct MyVeryLongNamedStruct: Selfy, Equatable {
  static func == (lhs: StaticSelf, rhs: StaticSelf) -> Bool {
    return true
  }
}

Was this possible in Swift 4.2? If not, was this exposure an accident? If it was, then I can only say 'source compatibility'. :smiley:

cc @Slava_Pestov @trs

This code works in a 4.2 playground.

1 Like

The behavior here has not changed between 4.2 and 5. Self inside a protocol or protocol extension is the conforming type.

Your UIView extension for example is equivalent to writing:

extension UIView {
  var _view: UIView {
    return self
  }
}
1 Like

By 'the conforming type' you mean "the type that you access a member on with Self" or "the very first type that conforms to the protocol", it sounds like the last option, but it‘s not true for protocol extensions.

Now that we have SE-0068, can a conforming type use Self instead of the own type when implementing protocol requirements with Self?

Also what does the exposure of Self from classes (they supposed to be dynamic) mean?

class A {
  typealias _Self = Self
  struct PseudoGeneric {
    var base: _Self
  }
}

I think the correct answer is the first option, but I'm not entirely clear we're using the same terminology. I'll try to explain the precise behavior.

First, this is all about "Self" inside protocols (and protocol extensions). "Self" behaves like a generic parameter type here. What does this mean? Well, first, let's look at an example involving a concrete generic type:

struct Generic<T> {
  typealias A = (T, T)
}

What does it mean when I write something like Generic<Int>.A? The compiler takes the underlying type for A, which is (T, T), and replaces each occurrence of T with its substitution from the base type Generic<Int>, which is Int. So Generic<Int>.A is (Int, Int).

Now let's suppose we have a protocol typealias, for example something like:

protocol P {
  typealias A = Array<Self>
}

struct S : P {}

When I write S.A, we again take the underlying type of A, which is Array<Self>, and substitute in a replacement type for Self. In this case though, instead of looking at the generic parameters of the base type S, the replacement type for a protocol's Self is S itself. So S.A is Array<S>.

In your first example,

extension UIView {
  var _view: StaticSelf {
    return self
  }
}

It helps to think of an unqualified reference like StaticSelf as having a base type of its context, which is UIView. So it's equivalent to declaring var _view: UIVIew.StaticSelf. Following the above rules, the result is UIView.

Similarly, if you have:

extension UIButton {
  var _button: StaticSelf {
    return self
  }
}

The reference to StaticSelf inside an extension of UIButton is equivalent to having written UIButton.StaticSelf, which is equivalent to UIButton.

Inside a struct or enum, Self is equivalent to writing out the name of the type. So yes, you can use Self in a concrete type context to implement a witness for a protocol requirement that has the protocol Self in its type.

Inside a class, Self is something different. It means the dynamic type of the self parameter; and in declaration signatures, it is only valid as the return type of a method, with at most one level of optionality. This has not changed between 5 and the swift-5.1-branch (which has the SE-0068 implementation).

What SE-0068 does allow you to do is to refer to Self inside the body of a method. Previously, you could manipulate values of type Self, define new variables who type contains Self via inference, and even call generic functions with a substitution involving Self, but you had no way to spell a type containing Self which sometimes required awkward workarounds. Now you can.

In your example, you define a typealias with an underlying type of Self inside a class:

This should be an error. Please file a bug. Eventually we can allow it, but it would require additional checks that were not added as part of SE-0068.

10 Likes

Thank you so much for taking the time and explaining Self to me. I don't know why but for years up until now I did not understand what Self really meant in protocols. That also made me realize why Generic<Self> in classes would not work, at least not now.

For anyone interested in a concrete example, here is one. Custom generic type are not co-variant and therefore this example must crash.

enum AnyDelayedImmutable {
  case uninitialized
  case initialized(Any)

  init() {
    self = .uninitialized
  }

  func getValue<T>() -> T {
    switch self {
    case .uninitialized:
      fatalError("property accessed before being initialized")
    case .initialized(let value):
      guard let transformed = value as? T else {
        fatalError("property cannot be cast to type \(T.self)")
      }
      return transformed
    }
  }

  mutating func setValue(_ value: Any) {
    switch self {
    case .uninitialized:
      self = .initialized(value)
    case .initialized:
      fatalError("property initialized twice")
    }
  }
}

struct Generic<T> {
  var value: T
}

protocol Container: AnyObject {}

class BaseContainer: Container {
  fileprivate var _generic = AnyDelayedImmutable()
}

extension Container where Self: BaseContainer {
  var generic: Generic<Self> {
    if case .uninitialized = _generic {
      let value = Generic<Self>(value: self)
      _generic.setValue(value)
    }
    return _generic.getValue()
  }
}

class B: BaseContainer {}

let b = B()
_ = b.generic
let container = (b as BaseContainer).generic // will crash

@johnno1962 @Slava_Pestov filed: https://bugs.swift.org/browse/SR-10334

1 Like
Terms of Service

Privacy Policy

Cookie Policy