2016-03-21 3 views
4

Я с ума сошел или не должен скомпилировать этот быстрый код?Включить класс Swift в протокол с тиалианами

protocol Protocol { 
    typealias Thing 
} 

class Class<X>: Protocol { 
    typealias Thing = X 
} 

func test<X:Protocol where X.Thing == Int>() -> X { 
    return Class<Int>() // error: cannot convert return expression of type 'Class<Int>' to return type 'X' 
} 

Я не могу показаться, чтобы привести объект к своему протоколу, хотя общий тип и aliastype матч.

EDIT:

я придумал с кодом выше, извлекая логику из моего существующего кода в целях упрощения задачи. Я сделал некоторые ошибки при этом. Вот обновленный (и, надеюсь, меньше путаницы) Пример кода:

protocol Protocol { 
    typealias Thing 
} 
class Class<X>: Protocol { 
    typealias Thing = X 
} 
func test<Y: Protocol where Y.Thing == Int>() -> Y { 
    return Class<Y.Thing>() 
} 

Я ожидал, что компилятор, чтобы тест() для компиляции с типом результата является Protocol<Int>.

+1

Определение вашей функции 'test' говорит, что ей нужно вернуть' X'. – Michael

ответ

6

Ваш тип возврата невозможно в сегодняшнем Swift. Протокол с ассоциированным типом (PAT) является абстрактным. Применение предложения where не изменяет это. Рассмотрим этот код:

let x: <WHAT-GOES-HERE?> = test() 

Какой бы x быть здесь? Там нет ничего, что можно было бы написать там, где можно было бы скомпилировать. Что ответит x.Type? То, что вы хотите, это Protocol where Protocol.Thing == Int, но это не тип Swift. Это тип ограничение. Это единственный способ использования PAT сегодня. Вот почему у вас не может быть свойства типа CollectionType<Int>, и почему вы не можете написать свою функцию test().

Решение является тире-ластиком для преобразования вашего протокола в конкретную структуру. Например:

protocol Protocol { 
    typealias Thing 
    func dosomething() -> Thing? 
} 
class Class<X>: Protocol { 
    typealias Thing = X 
    func dosomething() -> Thing? { 
     return nil 
    } 
} 

struct AnyProtocol<Thing> { 
    var _dosomething:() -> Thing? 
    func dosomething() -> Thing? { 
     return _dosomething() 
    } 
} 

func test() -> AnyProtocol<Int> { 
    return AnyProtocol(_dosomething: Class<Int>().dosomething) 
} 

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

Подробнее о строительстве и использовании типа ластиков см. A Little Respect for AnySequence.

+0

Отличный ответ и именно тот тип объяснений, который я получил после. –

1

Я с ума сошел или не должен скомпилировать этот быстрый код?

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

protocol Protocol { 
    typealias Thing 
} 
class Class<X>: Protocol { 
    typealias Thing = X 
} 
func test<Y:Protocol where Y.Thing == Int>() -> Class<Y> { 
    return Class<Y>() 
} 

Но несмотря на то, что компилирует, я до сих пор не вижу к чему вы клоните. Во-первых, я не вижу, как Y в test будет разрешен фактическим кодом. Возможно (судя по названию) проблема заключается в том, что вы ожидаете, что родовые типы будут защищены от других типов. Они не. Например, данный класс Animal и его подкласс Dog, вы не можете обменивать между MyGeneric<Animal> и MyGeneric<Dog>.

Ваш комментарий также не имеет смысла:

, что я пытаюсь сделать, это бросить Class<Int> к Protocol<Int>

Там нет такого типа, как Protocol<Int>. Есть только конкретные усыновители протокола, которые specialize Протокол, чтобы Thing Int.

+0

Если проблема в том, что я указываю в моем последнем абзаце, вы можете увидеть мой ответ здесь: http://stackoverflow.com/a/35787361/341994 – matt

+0

Извините, двойное использование X _is_ запутывает .. но то, что я пытаюсь сделать, это отличить класс к протоколу , который должен компилироваться, учитывая, что typealias класса связывает два типа вместе. –

+0

Нет, этого не должно быть. Нет такой вещи, как «Протокол ». Все понятие не имеет смысла. – matt

1

Причина, по которой она терпит неудачу, проста: вы не уважаете специфику, которую вы сами определили.

При использовании дженериков у вас есть общий тип (в данном случае X), что означает не просто «все, что удовлетворяет ограничениям», а «что-то конкретное, которое удовлетворяет ограничениям».

Итак, если вы делаете что-то вроде let foo: Class<Int> = test(), ваш код все равно может выглядеть в порядке. Но если вы делаете что-то вроде

class AnotherClass<X>: Protocol { 
    typealias Thing = X 
} 
let bar: AnotherClass<Int> = test() 

вы видите ясно, почему выше неправильно.

Основная концепция здесь, когда вы работаете с дженериками, вы можете использовать только то, что дает вам общий. В этом случае используйте только X и X.Thing, что является Int.Ничего другого, если не добавить что-то Protocol или другие ограничения в функции test

Update:

Я вижу теперь с обновленным вопросом. Посмотрите на ответ Роба Напира, что вам нужно, это стикер типа. Хотя я думаю, что речь в быстрой эволюции по экзистенциальным типам могла бы привести эту особенность?

+0

Я обновил код с тех пор, как совершил ошибку. Второй образец кода устанавливает возвращаемый тип 'test()' как 'Protocol, где Protocol.Thing == Int'. Я не ожидаю, что ваш код будет скомпилирован из-за кастинга, но я ожидаю, что смогу использовать возвращаемое значение, как если бы оно было ссылкой на протокол. –

+0

Вы видели фактическое предложение для экзистенциальных типов, или это все еще просто болтовня? (Надеюсь, в конце концов появится реальное предложение.) –

+0

@RobNapier Что такое экзистенциальный тип? И когда я спрашиваю об этом, я надеюсь, вы напишете объяснение в своем блоге! :) – matt

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