Rust HRTB идентичен, но компилятор говорит, что один тип более общий, чем другой

Следующий код полностью аннотирован:

fn enter<'a, F, R>(x: &'a i32, func: F) -> R
where
    F: for<'b> FnOnce(&'b i32) -> R,
{
    func(x)
}

fn identity<'a>(x: &'a i32) -> &'a i32 {
    x
}

fn main() {
    let x = &42;
    enter(x, identity);
}

Компилятор сообщает об ошибке при компиляции:

error[E0308]: mismatched types
  --> src/main.rs:14:5
   |
14 |     enter(x, identity);
   |     ^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'b> FnOnce<(&'b i32,)>`
              found trait `for<'a> FnOnce<(&'a i32,)>`
note: the lifetime requirement is introduced here
  --> src/main.rs:3:35
   |
3  |     F: for<'b> FnOnce(&'b i32) -> R,
   |                                   ^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

Насколько я понимаю, связанная черта for<'b> FnOnce(&'b i32) -> R является более общей, чем подпись identityfor<'a> fn(&'a i32) -> &'a i32, поэтому она должна пройти.

Тем не менее, общий -> R кажется виновником остановки компиляции, но я не знаю, почему.

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

Связанный:

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
0
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Насколько я понимаю, связанная черта for<'b> FnOnce(&'b i32) -> R является более общей, чем подпись identityfor<'a> fn(&'a i32) -> &'a i32, поэтому она должна пройти.

Нет это не так. Когда R объявляется, 'b находится вне контекста, поэтому не может зависеть от него (это один из способов взглянуть на это, есть и другие).

Это необходимо для того, чтобы такие API, как std::thread::LocalKey::with(), были надежными: если бы R было разрешено содержать время жизни (неявное в случае with()), ссылки на локальный поток могли бы избежать замыкание и with(), а затем используются в разных потоках, вызывая UB.

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

Другие вопросы по теме