TGViewer
Блог* Блог* @dereference_pointer_there · 1.98K subscribers
Post #446 558
#prog #rust #моё

В Rust есть много хороших вещей. Одна из них — функции как первоклассные значения. Мы можем передать функцию как аргумент, возвращать функцию из функции и даже хранить функции в коллекциях. К сожалению, нацеленность Rust на абстракции нулевой (дополнительной) стоимости создаёт некоторые препятствия к широкому применению этой возможности и часто создаёт вопросы у новичков. Какие могут быть проблемы? Ну... Давайте просто поиграем с функциями, а проблемы возникнут сами 😈.

Сделаем функцию, которая будет принимать на вход два аргумента: функцию из числа в число и число, и которая будет просто применять функцию к этому числу. Неискушённый человек, только постигающий Rust, может написать следующее:

//             vvvvvvvvvvvvvv - это же функция, верно?
fn apply(func: fn(u32) -> u32, x: u32) -> u32 {
func(x)
}

Попробуем её применить:

fn add_one(x: u32) -> u32 {
x + 1
}

fn main() {
println!("{}", apply(add_one, 3));
}

Этот код выводит "4", как и ожидается. Но постойте-ка, у нас же есть замечательный синтаксис для лямбда-функций! Может, применить его?

fn main() {
println!("{}", apply(|x| x + 1, 3));
}

Этот код тоже выводит "4". Что ж, давайте немного усложним код:

fn main() {
let one = 1;
println!("{}", apply(|x| x + one, 3));
}

И этот код выводит... А не, не выводит, он не компилируется:

error[E0308]: mismatched types
--> src/main.rs:8:26
|
8 | println!("{}", apply(|x| x + one, 3));
| ^^^^^^^^^^^ expected fn pointer, found closure
|
= note: expected fn pointer `fn(u32) -> u32`
found closure `[closure@src/main.rs:8:26: 8:37 one:_]`

Но ведь это же функция!

Да, но это не просто функция, это замыкание, т. е. функция, использующая данные за пределами е определения. Для того, чтобы вызвать это замыкание, требуется не только знать адрес, по которому расположен код функции, но и каким-то образом передать захваченные данные. Для каждой лямбда-функции компилятор Rust создаёт создаёт свой собственный уникальный тип. Поля этого типа — это данные, которые захватываются замыканием, в данном случае — поле под значение one. Тип же fn(u32) -> u32 — это функциональный указатель (function pointer), фактически, просто адрес в памяти, по которому располагается код функции. Неудивительно, что apply не удалось вызвать — это разные типы с разными размерами!

Но погодите, почему же удалось вызвать apply с аргументом |x| x + 1?

Потому что лямбда-функция |x| x + 1 не захватывает никаких переменных из окружения, а такие замыкания могут быть приведены к функциональному указателю начиная с версии Rust 1.19. Такое приведение может сработать в ряде различных мест, в число которых входит и аргумент функции при вызове.

Это всё, конечно, очень интересно, но как мне всё-таки вызвать apply с |x| x + one?

Очевидно, поменяв тип apply. Мы не можем дать типу замыканию имя, поэтому правильным решением будет сделать apply обобщённой функцией:

fn apply<F>(func: F, x: u32) -> u32
where
F: Fn(u32) -> u32,
{
func(x)
}

Что тут происходит? Мы ввели обобщённый параметр F и добавили на него ограничение Fn(u32) -> u32. Это означает, что значение типа F можно вызвать как функцию с одним аргументом u32, причём неограниченное число раз. Теперь apply можно вызвать в любом варианте:

fn main() {
println!("{}", apply(add_one, 3));
println!("{}", apply(|x| x + 1, 3));
let one = 1;
println!("{}", apply(|x| x + one, 3));
}

Отлично! Значит, замыкания имеют разные типы, а у обычных функций с одинаковыми сигнатурами типы одинаковые

Ну, не совсем:

type Pair<T> = (T, T);

fn add_one(x: u32) -> u32 {
x + 1
}

fn add_two(x: u32) -> u32 {
x + 2
}

fn main() {
let funcs: Pair<_> = (add_one, add_two);
}

Этот код не компилируется:

error[E0308]: mismatched types
--> src/main.rs:12:36
|
12 | let funcs: Pair<_> = (add_one, add_two);
| ^^^^^^^ expected fn item, found a different fn item
|
= note: expected fn item `fn(_) -> _ {add_one}`
found fn item `fn(_) -> _ {add_two}`
Wikipedia Closure (computer programming) technique for creating lexically scoped first class functions
More from @dereference_pointer_there
  1. Oct 7, 2026#meme про jab
  2. Oct 7, 2026"Famous car"
  3. Oct 7, 2026photo post
  4. Oct 7, 2026Во избежание оффтопа под постом о поиске работы срач дискуссию о криптовалютах обсуждайте…
  5. Oct 7, 2026Блог* pinned «Меня зовут Антон, и в настоящий момент я ищу работу. У меня 5 лет опыта в пр…
  6. Oct 7, 2026^Папищеки, обращаюсь к вам за помощью: поделитесь, пожалуйста, особенно если вы знаете, чт…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →