2017-02-16 3 views
0

Я использую AtomicBox от Matt Галлахера CwlUtils в небольшой обратный отсчет:Мутирование закрытия не компилируется, если оно сложнее, чем одно утверждение - как исправить?

class Countdown { 
    private let counter: AtomicBox<Int> 

    init(from start: Int) { 
     self.counter = AtomicBox(start) 
    } 

    func countDown() { 
     self.counter.mutate { $0 -= 1 } 
    } 
} 

Это работает просто отлично. Теперь предположим, что я хочу бросить ошибку, если счетчик уже достиг нуля:

func countDown() throws { 
    self.counter.mutate { 
     guard $0 > 0 else { 
      throw MyError.alreadyZero 
     } 

     $0 -= 1 
    } 
} 

Теперь swift build жалуется: (. Weird сообщение об ошибке, кстати, они обычно приходят лучше)

<unknown>:0: error: parameters may not have the 'var' specifier 
<unknown>:0: error: build had 1 command failures 
error: exit(1): /Applications/Xcode8.2/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f /Users/dhtp/Documents/CwlUtils/.build/debug.yaml 

Что здесь происходит? Обе версии мутируют внутреннее значение counter (что и передается параметру mutate) точно так же. Почему один законный, а другой нет?

Как я могу объявить закрытие так, чтобы оно скомпилировалось (и делает то, что я хочу)?

Я пробовал { (inout value) in ... }, но это дает ту же ошибку.

ответ

1

Закрытие, которое вы проходите, объявляется броском. Компилятор не может вывести параметр в закрытие, потому что он не соответствует типу замыкания, который мутирует ожидание.

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

self.counter.mutate { 
    guard $0 > 0 // Note the condition 
    else { print("I'm sorry Dave, I'm afraid I can't do that") ; return } 
    $0 -= 1 
} 

Сообщение об ошибке сбивает с толку. Вероятно, вам стоит поднять ошибку на Swift.org.

+0

Мое предположение заключается в том, что компилятор каким-то образом видит его в качестве модификации передаваемой функции 'mutate'. вид фанки. – PeejWeej

+0

[Готово] (https://bugs.swift.org/browse/SR-3973), спасибо! – Raphael

+1

@PEEJWEEJ Функция не бросания рассматривается как специализация функции бросания с теми же параметрами, то есть '(inout Int) -> Void' является подтипом' (inout Int) throws -> Void'. Таким образом, вы можете использовать функцию throw throw, где параметр объявляется как металирование, но вы не можете использовать функцию throwing, где параметр не объявляется как металирование. – JeremyP

0

Мы должны явно переобъявить, что параметр inout по какой-то причине:

self.counter.mutate { (value: inout Int) in 
    ... 
} 

будет работать, как ожидалось.

Ну, в конкретном случае мы не можем передать бросок закрытия до mutate, но это еще одна проблема.

+0

На самом деле, я не думаю, что это еще одна проблема. Я думаю, что именно потому, что закрытие, которое он пропускает, объявляется броском. Компилятор не может вывести параметр в закрытие, потому что он не соответствует типу закрытия, который мутирует. – JeremyP

+0

@JeremyP А, это имеет смысл. В этом случае сообщение «только» компилятора не помогает. Я заметил пару раз, что мы не получаем сообщений «не может вывести что-то полезное, потому что X», но вместо этого «[я молча предполагал себя в тупик, а теперь] есть некоторая ошибка Y». :/ – Raphael

Смежные вопросы