Swift: массив дженериков с типами, соответствующими Equatable

Как в Swift определить массив универсальных шаблонов с типом, соответствующим Equatable?

Пример:

struct File<T: Equatable> {
    public var lines: [T]
    private var lineCursor = 0
    public var currentLine: T {
        get { return lines[lineCursor] }
        set { lineCursor = lines.index(where: { $0 == newValue }) ?? 0 }
    }
}

struct Folder {
    public var files: [File]? // compile time error
}

→ Ссылка на универсальный тип «Файл» требует аргументов в ‹…>

… Пока я пробовал:

[File<Any>]

→ Тип «Любой» не соответствует протоколу «Равный».


[File<Any: Equatable>]

→ Последовательные объявления в строке должны быть разделены символом ';'


[File<Any, Equatable>]

→ Универсальный тип «Файл», специализирующийся на слишком большом количестве параметров типа (получено 2, но ожидается 1)


[File<Any & Equatable>]

→ Использование Equatable в качестве конкретного типа, соответствующего протоколу Equatable, не поддерживается.


[File<(Any: Equatable)>]

→ Невозможно создать одноэлементный кортеж с меткой элемента


[File<(Any, Equatable)>]

→ Тип '(Any, Equatable)' не соответствует протоколу 'Equatable'


[File<(Any & Equatable)>]

→ Использование Equatable в качестве конкретного типа, соответствующего протоколу Equatable, не поддерживается.


[File<[Any: Equatable]>]

→ «Файл» требует, чтобы «Equatable» соответствовал «Equatable»


[File<[Any, Equatable]>]

→ Последовательные объявления в строке должны разделяться знаком ';'


[File<[Any & Equatable]>]

→ «Файл» требует, чтобы «Equatable» соответствовал «Equatable»


Какой правильный синтаксис?


[EDIT] упростил пример


[РЕДАКТИРОВАТЬ] обновленный пример:

class File<T: Equatable> {
    var lines = [T]()
    var lineCursor: Int = 0
    var currentLine: T {
        get { return lines[lineCursor] }
        set { lineCursor = lines.index(where: { $0 == newValue }) ?? 0 }
    }
    var visible = true
}

class Folder {
    var files = [File]() // Generic parameter 'Type' could not be inferred; I want this to be a mixed array
    func currentLinesFromVisibleFiles() -> String {
        return files.filter({ $0.visible }).map({ String(describing: $0.currentLine) }).joined(separator: "/")
    }
}

var stringFile = File<String>()
stringFile.lines = ["strong", "string", "a", "b", "c"]
stringFile.currentLine = "string"
stringFile.visible = true

var intFile = File<Int>()
intFile.lines = [6, 12, 0, 489]
intFile.currentLine = 489
intFile.visible = true

var doubleFile = File<Double>()
doubleFile.lines = [92.12, 4.9753, 1.6]
doubleFile.currentLine = 92.12
doubleFile.visible = false

var boolFile = File<Bool>()
boolFile.lines = [true, false]
boolFile.currentLine = true
boolFile.visible = true

var folder = Folder()
folder.files = [stringFile, intFile, doubleFile, boolFile]

let output = folder.currentLinesFromVisibleFiles() // I want: "string/489/true"

person Rudy Phillipps    schedule 13.05.2018    source источник
comment
Какие типы значений вы собираетесь использовать с этим классом Data и File? String? Int? Это то, что вам нужно использовать, а не Any.   -  person rmaddy    schedule 13.05.2018
comment
Какой смысл в этом Data типе? Я действительно не вижу смысла определять тип с единственным свойством экземпляра с неограниченным универсальным типом. Почему бы вам просто не использовать public var lines: [Type] в своем File типе? Более того, копирование существующих встроенных имен типов - плохая идея, так как это, скорее всего, приведет к путанице для читателей вашего кода.   -  person Dávid Pásztor    schedule 13.05.2018
comment
@rmaddy: Я хотел бы использовать любой тип, который есть или будет доступен. Как Float и Bool, а также любые будущие пользовательские типы, которые являются Equatable.   -  person Rudy Phillipps    schedule 13.05.2018
comment
Хорошо, но когда вы создаете экземпляр File или Data, вам нужно сделать это с определенным (и Equatable) типом. Any не Equatable.   -  person rmaddy    schedule 13.05.2018
comment
@ DávidPásztor: спасибо за ваш комментарий. Этот код - просто пример, помогающий понять, что я хочу узнать. Я думаю, копируя существующие встроенные имена типов, вы имеете в виду Type?   -  person Rudy Phillipps    schedule 13.05.2018
comment
@rmaddy: значит, создание экземпляра с подстановочным знаком Equatable невозможно? Я имею в виду подстановочный знак, такой как Any, для любого типа. Подстановочный знак для любого типа, соответствующего Equatable.   -  person Rudy Phillipps    schedule 13.05.2018
comment
Было бы действительно полезно, если бы вы обновили свой вопрос с четким примером того, как вы собираетесь использовать эти классы File и Folder.   -  person rmaddy    schedule 13.05.2018
comment
@RudyPhillipps нет, я имел в виду Data. Проблема с вашим текущим подходом заключается в том, что не существует типа File без указания компилятору, какой тип вы оборачиваете в File в качестве универсального типа. Точно так же, как не существует типа Optional или Array без параметров общего типа. По сути, вы ищете File<Equatable>, но это также не может работать, поскольку вы не можете использовать Equatable как конкретный тип. Не зная точно, чего именно вы пытаетесь достичь, одним из возможных решений могло бы стать создание Equatable со стиранием типа и сохранение его массива в Folder.   -  person Dávid Pásztor    schedule 13.05.2018


Ответы (1)


Вы должны указать тип параметра T в [File<T>]?, чтобы компилятор мог пройти, обратите внимание, что вы пытаетесь создать однородный массив, который, как только вы укажете T в качестве последнего типа, вы не сможете смешивать типы в file, т.е. вы можете ' t смешать File<Int> и File<String>. если вам требуется соответствие Equatable для вычисления currentLine, вы можете использовать условное соответствие для добавления свойства динамически, погода или нет T равнозначна, как:

protocol HasCurrentLine{
    associatedtype LineType
    var currentLine: LineType { set get }
}

struct File<T> where T:Any {
    public var lines: [T]
    var lineCursor = 0
}

extension File : HasCurrentLine where T : Equatable{
    typealias LineType = T
    var currentLine: T {
        get { return lines[lineCursor] }
        set { lineCursor = lines.index(where: { $0 == newValue }) ?? 0 
    }
  }
}
struct Folder {
    public var files: [File<Any>]?
}

таким образом вы можете подсчитывать строки всякий раз, когда T равно Equatable

person Jans    schedule 13.05.2018
comment
Большое спасибо за ответ. Следуя этому примеру, я столкнулся с проблемой при попытке сохранить File ‹Int› () в массив файлов: Cannot convert value of type 'File<Int>' to expected element type 'File<Any>'. Я обновил пример в своем вопросе, чтобы уменьшить недопонимание. - person Rudy Phillipps; 14.05.2018