Я тоже борется с этим, решения set_error_handler
не работали для меня, возможно, на основе различий в версии PHP.
Решение для меня заключалось в попытке обнаружить ошибку при выключении :
// Since set_error_handler doesn't catch Fatal errors, we do this
function shutdown()
{
$lastError = error_get_last();
if (!empty($lastError)) {
$GLOBALS['logger']->debug(null, $lastError);
}
}
register_shutdown_function('shutdown');
Я не уверен, почему деление на 0 выключается, а не обрабатывается set_error_handler
, но это помогло мне выйти за пределы этого, просто молча умер.
Родовые ассоциированные типы (GAT) - это ассоциированные типы , которые сами являются родовыми . RFC начинается с мотивирующего примера , выделенного мной:
Рассмотрим следующую черту в качестве типичного мотивирующего примера:
trait StreamingIterator { type Item<'a>; fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>; }
Эта черта очень полезна - он допускает своего рода Итератор, который выдает значения, время жизни которых связано с временем жизни ссылки, переданной в
next
. Конкретным очевидным вариантом использования этой черты будет итератор по вектору, который дает перекрывающиеся изменчивые субликсы с каждой итерацией. При использовании стандартного интерфейсаIterator
такая реализация была бы недействительной, поскольку каждый срез должен был бы существовать столько же, сколько итератор, чем столько, сколько заимствование, инициированноеnext
.Эта черта не может быть выражена в Rust, как она существует сегодня , потому что она зависит от своего рода полиморфизма высшего рода. Этот RFC расширил бы Rust, чтобы включить эту специфическую форму полиморфизма с более высоким родом, который упоминается здесь как конструкторы связанного типа. Эта особенность имеет ряд применений, но основное применение имеет те же черты, что и черта
blockquote>StreamingIterator
: определение черт, которые дают типы, срок жизни которых связан с локальным заимствованием типа получателя.Обратите внимание, что связанный тип
Item
имеет общее время жизни'a
. Большинство примеров в RFC используют время жизни, но есть также пример, использующий универсальный тип :blockquote>trait PointerFamily { type Pointer<T>: Deref<Target = T>; fn new<T>(value: T) -> Self::Pointer<T>; }
Обратите внимание, что связанный тип
Pointer
имеет универсальный типT
.Ваш конкретный пример.
. Чем отличается
blockquote>Generic
от предыдущего примера от этого общего связанного типа. GAT не поможет вашему случаю, который, по-видимому, не требует ассоциированного типа, который сам по себе является общим.
Давайте снова посмотрим на ваш последний пример (сокращенный мной):
trait GenericAssociated {
type GenericAssociated;
}
impl<G> GenericAssociated for Struct {
type GenericAssociated = G;
}
Это не не имеет родственных связанных типов! У вас просто есть универсальный тип в вашем блоке impl
, который вы назначаете для связанного типа. Мм, хорошо, я вижу, откуда возникла путаница.
В вашем примере ошибки с «параметром типа G
не ограничены признаком impl, self-типом или предикатами». Это не изменится, когда GAT будут внедрены, потому что, опять же, это не имеет ничего общего с GAT.
Использование GAT в вашем примере может выглядеть так:
trait Associated {
type Associated<T>; // <-- note the `<T>`! The type itself is
// generic over another type!
// Here we can use our GAT with different concrete types
fn user_choosen<X>(&self, v: X) -> Self::Associated<X>;
fn fixed(&self, b: bool) -> Self::Associated<bool>;
}
impl Associated for Struct {
// When assigning a type, we can use that generic parameter `T`. So in fact,
// we are only assigning a type constructor.
type Associated<T> = Option<T>;
fn user_choosen<X>(&self, v: X) -> Self::Associated<X> {
Some(x)
}
fn fixed(&self, b: bool) -> Self::Associated<bool> {
Some(b)
}
}
fn main() {
Struct.user_choosen(1); // results in `Option<i32>`
Struct.user_choosen("a"); // results in `Option<&str>`
Struct.fixed(true); // results in `Option<bool>`
Struct.fixed(1); // error
}
Но чтобы ответить на ваш главный вопрос:
В чем разница между чертой универсальный тип и родовой ассоциированный тип?
blockquote>Вкратце: они позволяют отложить применение конкретного типа (или времени жизни), что делает всю систему типов более мощной.
Есть много мотивационных примеров в RFC , в частности, потоковый итератор и пример семейства указателей. Давайте быстро разберемся, почему потоковый итератор не может быть реализован с помощью обобщенных признаков.
GAT-версия потокового итератора выглядит следующим образом:
trait Iterator { type Item<'a>; fn next(&self) -> Option<Self::Item<'_>>; }
В текущем Rust мы можем поместить параметр времени жизни в признак вместо связанного с ним типа:
[1141 ] Пока все хорошо: все итераторы могут реализовать эту черту, как и раньше. Но что, если мы хотим использовать это?trait Iterator<'a> { type Item; fn next(&'a self) -> Option<Self::Item>; }
fn count<I: Iterator<'???>>(it: I) -> usize { let mut count = 0; while let Some(_) = it.next() { count += 1; } count }
Какое время жизни мы должны аннотировать? Помимо аннотирования времени жизни
'static
, у нас есть два варианта:
fn count<'a, I: Iterator<'a>>(it: I)
: это не сработает, потому что вызывающий объект выбирает универсальные типы функций. Ноit
(который станетself
в вызовеnext
) живет в нашем стековом фрейме. Это означает, что время жизниit
неизвестно вызывающей стороне. Таким образом мы получаем компилятор ( Playground ). Это не вариант.fn count<I: for<'a> Iterator<'a>>(it: I)
(с использованием HRTB): кажется, это работает, но у него есть тонкие проблемы. Теперь нам необходимоI
реализоватьIterator
для любого времени жизни'a
. Это не проблема для многих итераторов, но некоторые итераторы возвращают элементы, которые не существуют вечно, и, следовательно, они не могут реализоватьIterator
для любого времени жизни - просто время жизни короче, чем их элемент. Использование этих более высоких ранговых границ часто приводит к секретным'static
границам, которые очень ограничивают. Так что это тоже не всегда работает.Как видите, мы не можем правильно записать границы
I
. И на самом деле, мы даже не хотим упоминать время жизни в сигнатуре функцииcount
! Это не должно быть необходимо. И это именно то, что нам позволяют делать GAT (между прочим). С GAT мы могли бы написать:fn count<I: Iterator>(it: I) { ... }
И это будет работать. Потому что «применение конкретного времени жизни» происходит только тогда, когда мы называем
next
.Если вас интересует еще больше информации, вы можете взглянуть на мое сообщение в блоге «Решение проблемы обобщенного потокового итератора без GAT» , где я пытаюсь использовать универсальные типы для признаков, чтобы обойти недостаток ГАТ. И (спойлер): обычно это не работает.