Анонимные функции и блоки кода

Последнее обновление: 26.05.2024

Анонимные функции в отличие от обычных функций не имеют имени и могут сохраняться в переменную.

Формальное определение анонимной функции:

let переменная = | параметры | {
	// выполняемые действия
 };

Анонимная функция определяется без оператора fn. Ее определение фактически начинается с определения параметров, которые помещаются между двумя вертикальными чертами: | параметры |. Затем в фигурных скобках идут выполняемые действия. Причем после закрывающей фигурной скобки ставится точка с запятой. При этом анонимная функция сохраняется в переменной.

Вызов анонимной функции похож на вызов обычной функции, только для вызова применяется имя переменной:

название_замыкания(значения_для_параметров);

Рассмотрим простейший пример:

fn main(){
	let square = |n: i32|{
		let result = n * n;
		println!("Квадрат числа {} равен {}", n, result);
	};
	square(4);
	square(5);
	square(6);
}

Здесь определена переменная square, которая хранит анонимную функцию. Эта функция принимает один параметр типа i32 и выводит на консоль его квадрат.

Консольный вывод программы:

Квадрат числа 4 равен 16
Квадрат числа 5 равен 25
Квадрат числа 6 равен 36

Анонимные функции также могут возвращать значение:

fn main(){
    let sum = |a: i32, b: i32| -> i32{
		a + b
	};
	
	let sum_of_5_and_4 = sum(5, 4);
	println!("Сумма 5 и 4 равна: {}", sum_of_5_and_4);
}

Здесь определена переменная sum, которая представляет анонимную функцию, принимающую два параметра - два числа и возвращающую их сумму. И по имени переменной мы можем вызвать эту анонимную функцию и получить ее результат.

Если анонимная функция не принимает никаких параметров, то просто указываются две вертикальных черты:

let hello =||{			// определение анонимной функции
	println!("hello");
};
hello();	// вызов анонимной функции

Выведение типов

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

fn main(){
    let sum = |a, b|{	// типы параметров и результата не указаны
		a + b
	};
	let sum_of_5_and_4 = sum(5, 4);
	println!("Сумма 5 и 4 равна: {}", sum_of_5_and_4);
}

Более того, так как анонимная функция содержит лишь одно выполняемое действие, то мы можем и фигурные скобки убрать:

fn main(){
    let sum = |a, b| a + b;
	let sum_of_5_and_4 = sum(5, 4);
	println!("Сумма 5 и 4 равна: {}", sum_of_5_and_4);
}

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

fn main(){
    let sum = |a, b| a + b;
	let sum_of_int = sum(5, 4);
	let sum_of_float = sum(5.3, 4.2);
	println!("Сумма целых чисел: {}", sum_of_int);
	println!("Сумма дробных чисел: {}", sum_of_float);
}

Потому что после первого вызова анонимной функции sum() компилятор ожидает, что далее она будет принимать два целых числа. Однако во втором вызове в данном случае передаются два числа с плавающей точкой.

Блоки кода

От анонимных функций следуюет отличать блоки кода, которые присваиваются функциям. Так, рассмотрим следующий пример:

fn main(){
	let block = {
        println!("block starts");
        let n = 5;
        println!("n={n}");
        println!("block ends");
        6
    };

    println!("block={block}");
}

Здесь переменной block присваивается блок кода. В этом блоке кода могут идти разнообразные инструкции и выражения. Для примера я здесь вывожу некоторые диагностические сообщения и определяю переменную n. Блок кода, как и тело функции, заканчивается значением, которое возвращается из этого блока и именно это значение в реальности будет присваиваться переменной block. Посмотрим на консольный вывод:

block starts
n=5
block ends
block=6

Здесь мы видим, что блок кода выполняется сразу же при определении. А его возвращаемое значение - число 6 присваивается переменной block.

Посмотрим чуть более сложный пример с затенением:

fn main(){
	let n = {
        println!("block starts");
        let n = 5;
        println!("n in block={n}");
        println!("block ends");
    };

    println!("n in main={:?}", n);
}

Здесь блок кода присваивается переменной n, а внутри блока кода переопределяется переменная n. Какое значение в конечном счете после завершения блока получит переменная n? Посмотрим на консольный вывод:

block starts
n in block=5
block ends
n in main=()

Вне зависимости, как внутри блока определяется вложенная переменная n, внешняя переменная n получит то значение, которое возвращается блоком кода. А в данном случае блок коа явно не возвращает никакого значения, поэтому фактически это будет значение unit или ()

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850