Решение № 2 - единственный известный мне способ заставить эту работу работать с границами структуры.По моему мнению, заставить его работать без границ структуры, как Питер Холл предлагает , обычно предпочтительнее, потому что он ставит границы только там, где они действительно значимы, но если вы находите это обременительным, дополнительный параметр типа является вашей единственной возможностью.
Другая возможность - добавить параметр типа в структуру.Мне уже не нравится это решение, потому что параметр по своей природе не принадлежит структуре.
Второй параметр необходим.Типы аргументов Fn
-определяющего типа - это параметры Fn
черты , поэтому в принципе вы можете иметь как impl Fn(i32, i32) for X
, так и impl Fn(i32, String) for X
, так же как вы можете иметь оба impl AsRef<i32> for X
и impl AsRef<String> for X
.
На самом деле, если вы не смотрите на это слишком усердно, это как бы HRTB уже работают: функция может реализовать Fn(&'x i32)
для некоторых конкретных время жизни 'x
, или он может реализовать for<'a> Fn(&'a i32)
, что означает, что существует бесконечное число возможных Fn
черт, которые он реализует.
Но вы обнаружили проблему добавления параметра для P
: параметр не используется.
Эту проблему можно решить, добавив поле PhantomData<P>
, но в этом нет необходимости
Компилятородноранговые узлы внутри структур для определения дисперсии их параметров. В этом случае предположим, что P
является ссылочным типом.Безопасно ли передавать Foo<_, &'static T>
функции, ожидающей Foo<_, &'a T>
?А как же наоборот?
(Как указано в связанном ответе, ограничения - предложения where
- не учитываются при определении дисперсии, поэтому PhantomData
здесь необходимо.)
Но PhantomData
член не должен быть PhantomData<P>
, потому что Foo<_, P>
не содержит P
.Он содержит функцию, которая принимает P
в качестве аргумента .Вместо этого вы должны использовать PhantomData<fn(P)>
, который сигнализирует компилятору, что дисперсия Foo<F, P>
в P
совпадает с дисперсией fn(P)
- функция (указатель) принимает P
.Другими словами, Foo
является контравариантным в P
.Читателю-человеку это может показаться излишним - в конце концов, у нас уже есть член F
, а F
должен быть контравариантным в P
.Но, ну, в общем, компилятор не настолько умен, чтобы сделать такой вывод, поэтому вы должны изложить его.
(см. раздел Nomicon по подтипу для более точногообъяснение отклонений.)
Что подводит меня к вашему последнему возражению:
и, что более важно, пользователи больше не могут легко использовать синтаксис конструктора структуры.
К сожалению, я не могу придумать решение этой проблемы, кроме "написать хорошую функцию конструктора".Возможно, умный компилятор однажды снимет эту нагрузку, но сейчас PhantomData
- это то, что у нас есть.