
Дело в том, что у каждой функции — не только у замыканий — в Rust свой собственный уникальный тип. Здесь мы попытались сохранить значения двух разных типов в паре одинаковых типов, поэтому наш код не компилируется.
Зачем так сложно? В этом же нет никакого смысла!
Вообще-то есть. Компилятор эксплуатирует тот факт, что тип населён единственным значением, и потому не хранит и не передаёт адрес функции, а вставляет его по месту вызова. Таким образом, компилятору предоставляется больше возможностей для встраивания (inlining) функций. Также это влияет на структуры, которые хранят в себе функции: если тип не указан явно как функциональный указатель, обычная функция вообще не занимает места в памяти. Функциональный указатель же занимает столько же места, сколько и указатель:
fn main() {Кстати, на замыкания это тоже распространяется: значение замыкания хранит в себе только захваченные данные, поэтому размер замыкания равен (с поправкой на выравнивание) размеру захваченных данных. Если замыкание ничего не захватывает, то у него нулевой размер:
assert_eq!(std::mem::size_of_val(&add_one), 0);
let add_one_ptr: fn(u32) -> u32 = add_one;
assert_eq!(
std::mem::size_of_val(&add_one_ptr),
std::mem::size_of::<usize>()
);
}
fn main() {Теперь я вижу, что в этом действительно есть смысл. И всё-таки, как скомпилировать тот пример с парой функций?
assert_eq!(std::mem::size_of_val(&|x: u32| x + 1), 0);
}
Просто хранить разнородную пару.
А если нам принципиально, чтобы элементы пары имели одинаковый тип?
В таком случае нам надо привести значения к одинаковому типу. Это можно сделать так:
let funcs: Pair<fn(_) -> _> = (add_one, add_two);Обратите внимание, выписывать тип точно в этом случае не нужно, достаточно сказать, что это function pointer. Другой способ — явно привести одно из значений в паре:
let funcs: Pair<_> = (add_one as fn(_) -> _, add_two);Второе значение будет неявно приведено (coerced) к нужному типу. В принципе, ничто не мешает написать явный каст у обоих значений, но надобности в этом в данном примере нет. Кстати, если мы сделаем массив функций, то никаких кастов делать не придётся, rustc сам уравняет типы до функционального указателя:
let arr = [add_one, add_two];А что, если замыкание возвращает захваченное значение? Если это значение не копируемо, то замыкание можно вызвать только один раз...
А вот про это я расскажу в следующий раз.