Обобщённые функции
Rust поддерживает обобщения, которые позволяют абстрагировать алгоритмы или структуры данных (например, сортировку или двоичное дерево) от типов, которые в них используются или хранятся.
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
fn pick<T>(cond: bool, left: T, right: T) -> T {
if cond { left } else { right }
}
fn main() {
println!("picked a number: {:?}", pick(true, 222, 333));
println!("picked a string: {:?}", pick(false, 'L', 'R'));
}
-
Может быть полезно показать мономорфизированные версии
pick— либо до разговора об обобщённомpick, чтобы показать, как обобщения уменьшают дублирование кода, либо после разговора об обобщениях, чтобы показать, как работает мономорфизация.#![allow(unused)] fn main() { // Copyright 2023 Google LLC // SPDX-License-Identifier: Apache-2.0 fn pick_i32(cond: bool, left: i32, right: i32) -> i32 { if cond { left } else { right } } fn pick_char(cond: bool, left: char, right: char) -> char { if cond { left } else { right } } } -
Rust выводит тип для
Tна основе типов аргументов и возвращаемого значения. -
В этом примере для
Tмы используем только примитивные типыi32иchar, но здесь можно использовать любой тип, в том числе пользовательские:// Copyright 2023 Google LLC // SPDX-License-Identifier: Apache-2.0 struct Foo { val: u8, } pick(false, Foo { val: 7 }, Foo { val: 99 }); -
Это похоже на шаблоны C++, но Rust частично компилирует обобщённую функцию сразу, поэтому функция должна быть корректна для всех типов, удовлетворяющих ограничениям. Например, попробуйте изменить
pick, чтобы он возвращалleft + right, еслиcondложно. Даже если используется только версияpickдля целых чисел, Rust всё равно считает код некорректным. C++ позволил бы сделать это. -
Обобщённый код превращается в необобщённый на основе точек вызова. Это абстракция с нулевой стоимостью: вы получаете ровно тот же результат, как если бы вручную написали структуры данных без абстракции.