Странное поведение необязательного типа в swift forEach

#swift

#быстрый #swift

Вопрос:

Этот код работает нормально. Он повторяет мой массив из единицы Int! и выводит его величину.

 import Foundation

let x : Int! = 1

[x].forEach {i in
    print(i.magnitude)
}
  

Вывод:

 1
  

Предположительно, i в теле цикла есть Int или Int! , и действительно, если я попрошу Xcode предоставить «быструю справку» по forEach его отчетам:

 func forEach(_ body: (Int) throws -> Void) rethrows
  

Однако, если выполнить два оператора в моем forEach теле, вместо этого он не сможет скомпилироваться, жалуясь, что мне нужно развернуть, i который теперь имеет необязательный тип Int? .

 import Foundation

let x : Int! = 1

[x].forEach {i in
    print(i.magnitude)
    print(i.magnitude)
}
  

Ошибка компиляции:

 Value of optional type 'Int?' must be unwrapped to refer to member 'magnitude' of wrapped base type 'Int'
  

И если я попрошу «быструю помощь» сейчас, я получу:

 func forEach(_ body: (Int?) throws -> Void) rethrows
  

Каким образом, черт возьми, количество операторов, которые я помещаю в тело моего цикла, влияет на тип переменной цикла?

Комментарии:

1. Вывод типа плюс неявная необязательность не распространяются. Это всего лишь цена, которую мы платим за то, как опции внедряются в язык. Вы можете исправить это с помощью явного ввода.

2. Компилятор имеет быстрый путь для замыканий с одним выражением. Замыкания с несколькими выражениями не выигрывают от этого и нуждаются в явной аннотации типа. То, что вы видите здесь, — это компилятор, пытающийся определить типы.

Ответ №1:

По сути, вы выявили крайний случай крайнего случая. Вы объединили две вещи, которые являются работой дьявола:

  • Неявно развернутые опции
  • Неявный вывод типа замыканий, наряду с тем фактом, что
    • Вывод неявного типа замыканий работает по-другому, когда замыкание состоит из одной строки (именно здесь возникает вопрос «Как, черт возьми, количество операторов»)

Вы должны попытаться избежать обоих из них; ваш код будет чище и будет компилироваться намного быстрее. Действительно, неявный вывод типа чего-либо, кроме одного литерала, такого как string, Int или Double, сильно затягивает время компиляции.

Я не буду притворяться, что имитирую рассуждения компилятора; Я просто покажу вам реальное решение (кроме того, что я вообще не использую IUO).:

     [x].forEach {(i:Int) in
        print(i.magnitude)
        print(i.magnitude)
    }
  

Наш Int тип является законным, потому что мы используем единственную карточку «выйти из тюрьмы бесплатно», в которой говорится, что неявно развернутый необязательный вариант может быть использован непосредственно там, где ожидается сам развернутый тип. И, явно указывая тип, мы устраняем сомнения компилятора.

(Я говорю «напрямую», потому что неявная развернутость необязательного не распространяется через передачу и присвоение. Вот почему во втором примере вы обнаружили, что Int? не Int передается в закрытие.)

Комментарии:

1. Возможно, вас заинтересует мой запрос на bugs.swift.org/browse/SR-13107 которое, если вы правильно об этом подумаете, на самом деле связано с этой ситуацией.

2. Отличный ответ! Чтобы кое-что прояснить: в версии этого кода, не являющейся примером, массив представляет собой раскадровку IBOutlets (которую xcode создает как неявно развернутые опции по достаточно разумным причинам). Это не позволяет мне избегать вашего первого пункта!

3. Любопытно: { (i:Int) in ... } подразумевает ли это -> Void (как это происходит с такими простыми функциями, как func f() { ... } ), или это оставляет возвращаемый тип неопределенным и, следовательно, требует вывода?

4. @Alexander-RestorateMonica Это известно из объявления forEach .

5. @JamieCockburn Вопрос о том, должны ли торговые точки быть IUOS, является религиозной войной.