В Rust очень мощная система сопоставления с образцом. Вы можете сопоставлять литералы, структуры, перечисления, срезы (различной длины), определенные поля в структуре (путем деконструирования), вложенные поля, ссылки, диапазоны и т. Д.
Предполагая, что мы хорошо разбираемся в сопоставлении с образцом в Rust, давайте рассмотрим ключевое слово ref
и взаимосвязь между ref
и &
, когда дело доходит до сопоставления с образцом.
Eg1:
let x:i32 = 1; match x { ref y => println!("{}", *y), _ => () }
Приведенный выше фрагмент является очень простым примером (Очевидно, что условие «по умолчанию» никогда не будет выполнено. Но здесь мы можем проигнорировать это в интересах этой темы). В основном ref
вступает в игру после матча. т.е. «Сопоставить, а затем рассматривать связанную переменную как ссылку». Здесь переменная y
привязана к ссылке x
после совпадения.
Давайте посмотрим на следующий пример:
Eg2:
let x:i32 = 1; match &x { y => println!("{}", *y), _ => () }
Здесь операнд для match
является ссылкой, что означает, что переменная y
будет привязана ко всему операнду. Вот еще один пример,
Eg3:
let x:i32 = 1; match &x { &y => println!("{}", y), _ => () }
Операнд &x
деформируется в случае (&y
), и только часть значения y
извлекается (копируется). Обратите внимание на разницу; &
принимает участие в процессе сопоставления, а ref
- нет. В этом примере будет ошибка компиляции, если операнд не является ссылкой.
Eg1 лучше с точки зрения удобочитаемости. На самом деле нет необходимости явно брать ссылку и передавать ее блоку match
, а что, если тип не Copy
? Eg3 не будет компилироваться, так как вы не можете переместить значение, если оно находится за ссылкой.
Давайте посмотрим еще на несколько примеров.
Вот пример, в котором мы деконструируем настраиваемый тип (перечисление) и берем ссылки на поля его вариантов.
enum Shape { Circle(u8), Rectangle(u8, u8), Triangle(u8, u8) } // where `shape` is an instance of `Shape` match shape { // `radius` is of type &u8, due to ref Circle(ref radius) => circle_area(radius), // `length` and `width` are of type &u8, due to ref Rectangle(ref length, ref width) => rect_area(length, width), // `base` and `height` are of type &u8, due to ref Triangle(ref base, ref height) => triangle_area(base, height) }
Вот пример, в котором мы деконструируем struct
, а затем сопоставляем одно из его полей и берем ссылку на другое.
enum FoodType { Soda, Pizza } struct Details { name : String, calories : u16, } struct Food { category : FoodType, details : Details } fn consume(p: Food) { match p { Food { category: Soda, details: ref d, } => sip(d), Food { category: Pizza, details: ref d, } => bite(d), } }
Что, если вместо этого мы передадим &p
блоку match
? Будет ли это компилироваться?
Да! он компилируется. Обратите внимание на образцы в корпусах. Все они являются значениями, поэтому для сопоставления компилятор сначала выполняет автоматическое разыменование (*p
). Если бы он был &&p
, то он дважды автоматически разыграл бы и так далее ...
Подразумевается, что d
является ссылкой, поскольку операнд был ссылкой.
Итак, нужно ли нам оставить ключевое слово ref
? Хотя код все еще компилируется, ref
в этом сценарии является избыточным, и он нам здесь не нужен.
fn consume(p: Food) { match &p { // could very well be &&p, &&&p ... Food { category: Soda, details: d, // `ref d` or `d`,both are same i.e &Details } => sip(d), Food { category: Pizza, details: d, // `ref d` or `d`,both are same i.e &Details } => bite(d), } }
Другое менее известное использование (вне сопоставления с образцом) - это ссылка на переменную.
В основном следующий фрагмент:
let y = &mut x;
можно записать как
let ref mut y = x;
Вот и все! Надеюсь, эта краткая статья окажется для вас информативной.