Swift - укажите соответствие протоколу параметра универсального типа.

То, что я пытаюсь сделать, - это иметь два параметра универсального типа, один из которых является конкретным типом, а другой - таким протоколом:

@propertyWrapper
struct Implementation<T, P> where T : AnyObject, T : P { // Compiler error

    var wrappedValue: P { projectedValue }

    var projectedValue: T

    init(_ instance: T) {
        self.projectedValue = instance
    }

}

Таким образом, фактический тип может быть скрыт, и будет открыт только протокол.

Теперь это не работает, потому что P - это неклассовый, не протокольный тип, поэтому T не может быть ограничен им.

Это можно обойти?


person funct7    schedule 22.04.2020    source источник


Ответы (3)


Я думаю, вы можете создать протокол для наследования T, тогда вам вообще не понадобится P:

protocol ImplementationProtocol: AnyObject {}

@propertyWrapper
struct Implementation<T: ImplementationProtocol> { 

    var wrappedValue: ImplementationProtocol { projectedValue }

    var projectedValue: T

    init(_ instance: T) {
        self.projectedValue = instance
    }

}

теперь ваш «T» должен будет соответствовать «ImplementationProtocol», а «wrappedValue» также должен будет соответствовать «ImplementationProtocol», как вы пытались выполнить в приведенном выше коде.

Надеюсь, это поможет

person Aviv Frenkel    schedule 22.04.2020
comment
Это не то, что я пытаюсь сделать. Эту оболочку свойств можно использовать только для ImplementationProtocol. - person funct7; 22.04.2020
comment
хорошо, я немного покопался, как насчет этого подхода, он не идеален, так как wrappedValue имеет значение NULL, но он должен работать - person Aviv Frenkel; 22.04.2020

То, что вы хотите, не является особенностью языка, поэтому ближайший вариант - это решение среды выполнения, которое сводит на нет некоторые сахара-оболочки свойств.

@propertyWrapper
struct Implementation<Object: AnyObject, Protocol> {
  init(_ projectedValue: Object) throws {
    if let error = CastError.Desired(projectedValue, Protocol.self)
    { throw error }

    self.projectedValue = projectedValue
  }

  var projectedValue: Object
  var wrappedValue: Protocol { projectedValue as! Protocol }
}
protocol Protocol { }
class Class: Protocol { init() { } }
struct Struct {
  @Implementation<Class, Protocol> var implementation: Protocol

  init() throws {
    _implementation = try .init( .init() )
  }
}
public enum CastError {
    /// An error that represents that an desired cast is not possible.
  public struct Desired<Instance, DesiredCast>: Error {
    /// `nil` if `instance` is a `DesiredCast`.
    /// - Parameter instance: Anything. ????
    public init?(_ instance: Instance, _: DesiredCast.Type) {
      if instance is DesiredCast
      { return nil }
    }
  }

  /// An error that represents that an undesired cast is possible.
  public struct Undesired<Instance, UndesiredCast>: Error {
    /// `nil` if `instance` is not an `UndesiredCast`.
    /// - Parameter instance: Anything. ????
    /// - Note: Ineffective if `instance` is a protocol instance
    /// and `UndesiredCast` is `AnyObject`.
    public init?(_ instance: Instance, _: UndesiredCast.Type) {
      guard type(of: instance) is UndesiredCast.Type
      else { return nil }
    }
  }
}
person Jessy    schedule 22.04.2020
comment
Да, я думаю, это лучшее, что мы можем сделать с текущими языковыми ограничениями. Очень жаль :( - person funct7; 22.04.2020

person    schedule
comment
Это также ограничено тем, что нет возможности принудительно применить T также к типу P. По сути, это то же самое, что удалить T и создать instance: AnyObject. Тем не менее, спасибо за попытку. - person funct7; 22.04.2020