Я пытаюсь реализовать черту квадранта для двумерной точки и не понимаю, как сопоставить более двух полей структуры с диапазонами. Моя структура определяется как:
#[derive(Debug)]
struct Point<T, U> {
x: T, // so I can have float and int for x and y, sure
y: U,
}
impl<T, U> Point<T, U> {
fn x(&self) -> &T {
&self.x // make convenient accessors
}
fn y(&self) -> &U {
&self.y
}
}
Я определяю эту черту:
pub trait Quadrant {
fn quadrant(&self) -> String; // define the trait
}
И я пытаюсь реализовать это для структуры Point:
//now implement the trait for your point
impl<T,U> Quadrant for Point<T,U> {
fn quadrant(&self) -> String {
match self {
(self::x() > 0 && self::y() > 0) => "I",
(self::x() > 0 && self::y() < 0) => "IV",
(self::x() < 0 && self::y() > 0) => "II",
(self::x() < 0 && self::y() < 0) => "III",
(self::x() == 0 && self::y() == 0) => "ORIGIN",
_ => "AXIS",
}
}
}
Моя ошибка:
(self::x() > 0 && self::y() > 0) => "I",
^ expected one of `)`, `,`, or `|`, found `>`
Мне непонятно, как сделать этот блок соответствия на self.x() и self.y() для всех диапазонов.
Я хотел бы иметь возможность сказать:
fn main() {
let p1 = Point { x: 5, y: 10 };
println!("p1 is in Quadrant = {:?}", p1.quadrant());
}
С выходом p1 is in Quadrant I.

Плечи спичечного блока должны иметь форму <pattern> [conditions]. Если все, что у вас есть, это условия, вы можете написать старую простую цепочку if-else:
fn quadrant(&self) -> &'static str {
if self.x() > 0 && self.y() > 0 {
"I"
} else if self.x() < 0 && self.y() > 0 {
"II"
} else if self.x() < 0 && self.y() < 0 {
"III"
} else if self.x() > 0 && self.y() < 0 {
"IV"
} else if self.x() == 0 && self.y() == 0 {
"ORIGIN"
} else {
"AXIS"
}
}
Чтобы блок match имел смысл, вам нужно вытащить одно или несколько значений, чтобы было что-то, с чем можно сопоставить шаблон. Вы можете поместить координаты x и y в кортеж:
fn quadrant(&self) -> &'static str {
match (self.x(), self.y()) {
(x, y) if x > 0 && y > 0 => "I",
(x, y) if x < 0 && y > 0 => "II",
(x, y) if x < 0 && y < 0 => "III",
(x, y) if x > 0 && y < 0 => "IV",
(0, 0) => "ORIGIN",
_ => "AXIS",
}
}
Это работает, но это просто замаскированная цепочка if-else. Было бы лучше, если бы он не повторял сравнения в каждой руке. Мы можем сделать его правильным match блоком, вызвав cmp и сопоставив значения Ordering:
fn quadrant(&self) -> &'static str {
use std::cmp::Ordering::{Equal, Greater, Less};
match (self.x().cmp(&0), self.y().cmp(&0)) {
(Greater, Greater) => "I",
(Less, Greater) => "II",
(Less, Less) => "III",
(Greater, Less) => "IV",
(Equal, Equal) => "ORIGIN",
(Equal, _) | (_, Equal) => "AXIS",
}
}
Что мне нравится в этом, так это то, что становится ясно, что "AXIS" — это то, где одно значение или другое равно нулю. Написав это таким образом, а не с помощью универсальной _ => "AXIS" руки, компилятор может проверить, что мы рассмотрели все возможности. Если бы мы пропустили случай, код не скомпилировался бы.
Другие примечания:
Вызывайте методы получателя с помощью self.x(), а не self::x(). Двоеточия предназначены для вызова статических методов, как в Self::x(), если x не принимает параметр &self.
Если вы вернете &'static str вместо String, вы можете напрямую вернуть литералы. Если возвращаемый тип — String, вам нужно вызвать String::from или .into() или один из других методов String-конструкции, чтобы преобразовать статические литералы в выделенные в куче Strings.
Еще лучше сделать Quadrant перечислением вместо признака, чтобы вы могли возвращать строго типизированные значения перечисления.
Я бы рекомендовал сделать x и y общедоступными переменными и отказаться от геттеров. Сокрытие переменных и предоставление геттеров — это антипаттерн в Rust. Это не всегда неправильно, но это не то, что следует делать обычно, как в объектно-ориентированном языке, который способствует тяжелой инкапсуляции.
struct Point<T, U> {
pub x: T,
pub y: U,
}
Это отличный ответ. Думаю, я подниму свои последующие вопросы во втором вопросе. Спасибо!
Это имеет большой смысл. Когда я делаю x и y общедоступными, я получаю эту ошибку, которую я не понимаю: `match (self.x.cmp(&0), self.y.cmp(&0)) { ^^^
Tне является итератором `