1055 lines
26 KiB
Text
1055 lines
26 KiB
Text
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Operator overloading\n",
|
||
"\n",
|
||
"Rust allows you to overload operators by implementing specific traits from the `std::ops` module."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"| Category | Trait | Operator |\n",
|
||
"| -------------------- | -------------------------- | --------------------------------- |\n",
|
||
"| Unary operators | `std::ops::Neg` | `-x ` |\n",
|
||
"| | `std::ops::Not` | `!x ` |\n",
|
||
"| Arithmetic operators | `std::ops::Add` | `x + y` |\n",
|
||
"| | `std::ops::Sub` | `x - y` |\n",
|
||
"| | `std::ops::Mul` | `x * y` |\n",
|
||
"| | `std::ops::Div` | `x / y` |\n",
|
||
"| | `std::ops::Rem` | `x % y` |\n",
|
||
"| Bitwise operators | `std::ops::BitAnd` | `x & y` |\n",
|
||
"| | `std::ops::BitOr ` | `x \\| y` |\n",
|
||
"| | `std::ops::BitXor` | `x ^ y` |\n",
|
||
"| | `std::ops::Shl ` | `x << y` |\n",
|
||
"| | `std::ops::Shr ` | `x >> y` |\n",
|
||
"| Compound assignment arithmetic operators | `std::ops::AddAssign` | `x += y` |\n",
|
||
"| | `std::ops::MulAssign` | `x *= y` |\n",
|
||
"| | `std::ops::SubAssign` | `x -= y` |\n",
|
||
"| | `std::ops::DivAssign` | `x /= y` |\n",
|
||
"| | `std::ops::RemAssign` | `x %= y` |\n",
|
||
"| Compound assignment bitwise operators | `std::ops::BitAndAssign` | `x &= y` |\n",
|
||
"| | `std::ops::BitOrAssign ` | `x \\| = y` |\n",
|
||
"| | `std::ops::BitXorAssign` | `x ^= y` |\n",
|
||
"| | `std::ops::ShlAssign ` | `x <<= y` |\n",
|
||
"| | `std::ops::ShrAssign ` | `x >>= y` |\n",
|
||
"| Comparison | `std::cmp::PartialEq ` | `x == y`, `x != y` |\n",
|
||
"| | `std::cmp::PartialOrd` | `x < y, x <= y, x > y, x >= y` |\n",
|
||
"| Indexing | `std::ops::Index` | `x[y], &x[y]` |\n",
|
||
"| | `std::ops::IndexMut` | `x[y] = z, &mut x[y]` |"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"#[derive(Debug, Clone, Copy)]\n",
|
||
"struct Complex<T> {\n",
|
||
" re: T,\n",
|
||
" im: T,\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"let z: Complex<f64> = Complex { re: 1.0, im: 2.0 };"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "an implementation of `Add` might be missing for `Complex<f64>`",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31mError:\u001b[0m an implementation of `Add` might be missing for `Complex<f64>`"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"z + z"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Implementing `Add` trait"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" trait Add<RHS=Self> {\n",
|
||
" type Output;\n",
|
||
" fn add(self, rhs: RHS) -> Self::Output;\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::ops::Add;\n",
|
||
"\n",
|
||
"impl<T> Add for Complex<T> \n",
|
||
"where T: Add<Output = T> {\n",
|
||
" type Output = Self; \n",
|
||
" \n",
|
||
" fn add(self, rhs: Complex<T>) -> Self::Output {\n",
|
||
" Complex {\n",
|
||
" re: self.re + rhs.re,\n",
|
||
" im: self.im + rhs.im,\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"Complex { re: 2.0, im: 4.0 }"
|
||
]
|
||
},
|
||
"execution_count": 8,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"z + z"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Implementing `Neg` trait"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" trait Neg {\n",
|
||
" type Output;\n",
|
||
" fn neg(self) -> Self::Output;\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "an implementation of `std::ops::Neg` might be missing for `Complex<f64>`",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31mError:\u001b[0m an implementation of `std::ops::Neg` might be missing for `Complex<f64>`"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"-z"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::ops::Neg;\n",
|
||
"\n",
|
||
"impl<T> Neg for Complex<T>\n",
|
||
"where T: Neg<Output = T> {\n",
|
||
" type Output = Self;\n",
|
||
" \n",
|
||
" fn neg(self) -> Self::Output {\n",
|
||
" Complex {\n",
|
||
" re: -self.re,\n",
|
||
" im: -self.im,\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"Complex { re: -1.0, im: -2.0 }"
|
||
]
|
||
},
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"-z"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Compound Assignment Operators: `+=`, `-=`, `*=`, `/=`, `%=`"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" trait AddAssign<RHS=Self> {\n",
|
||
" fn add_assign(&mut self, rhs: RHS);\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::ops::AddAssign;\n",
|
||
"\n",
|
||
"impl<T> AddAssign for Complex<T>\n",
|
||
"where T: AddAssign<T> {\n",
|
||
" fn add_assign(&mut self, rhs: Complex<T>) {\n",
|
||
" self.re += rhs.re;\n",
|
||
" self.im += rhs.im;\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"Complex { re: 4.0, im: 6.0 }"
|
||
]
|
||
},
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"let mut c = Complex { re: 1.0, im: 2.0 };\n",
|
||
"\n",
|
||
"c += Complex { re: 3.0, im: 4.0 };\n",
|
||
"\n",
|
||
"c"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Equality and Ordering\n",
|
||
"\n",
|
||
"### PartialEq\n",
|
||
"\n",
|
||
"* in order to use `==` and `!=` operators, implement `PartialEq` trait"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" trait PartialEq<RHS=Self> \n",
|
||
" where RHS: ?Sized\n",
|
||
" {\n",
|
||
" fn eq(&self, other: &RHS) -> bool; \n",
|
||
" fn ne(&self, other: &RHS) -> bool { !self.eq(other) }\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::cmp::PartialEq;\n",
|
||
"\n",
|
||
"impl<T: PartialEq> PartialEq for Complex<T> {\n",
|
||
" fn eq(&self, other: &Complex<T>) -> bool {\n",
|
||
" self.re == other.re && self.im == other.im\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* now we can check for equality using `==` and `!=` operators"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"true"
|
||
]
|
||
},
|
||
"execution_count": 18,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"z == z"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"true"
|
||
]
|
||
},
|
||
"execution_count": 19,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"z != c"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"#### PartialEq & f64\n",
|
||
"\n",
|
||
" * Mathematical definition of an equivalence relation, of which equality is one instance, imposes three requirements. For any values `x` and `y`:\n",
|
||
" * If `x == y` is true, then `y == x` must be true as well. In other words, swapping the two sides of an equality comparison doesn’t affect the result.\n",
|
||
" * If `x == y` and `y == z`, then it must be the case that `x == z`. Given any chain of values, each equal to the next, each value in the chain is directly equal to every other. Equality is contagious.\n",
|
||
" * It must always be true that `x == x`.\n",
|
||
"\n",
|
||
" Types `f32` and `f64` are IEEE standard floating-point values. According to that standard, expressions like `0.0/0.0` and others with no appropriate value must produce special not-a-number values, usually referred to as `NaN` values. The standard further requires that a `NaN` value be treated as unequal to every other value—including itself."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"assert!(f64::is_nan(0.0 / 0.0));\n",
|
||
"\n",
|
||
"assert_eq!(0.0/0.0 == 0.0/0.0, false);\n",
|
||
"assert_eq!(0.0/0.0 != 0.0/0.0, true);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"NaN == NaN yields false\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"println!(\"{:?} == {:?} yields {:?}\", 0.0/0.0, f64::NAN, 0.0/0.0 == f64::NAN);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"While Rust’s `==` operator meets the first two requirements for equivalence relations, it clearly doesn’t meet the third when used on IEEE floating-point values. This is called a partial equivalence relation, so Rust uses the name `PartialEq` for the `==` operator’s built-in trait."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Eq - Full equivalence relation\n",
|
||
"\n",
|
||
"If you’d prefer your generic code to require a full equivalence relation, you can instead use the `std::cmp::Eq` trait as a bound, which represents a full equivalence relation: if a type implements `Eq`, then `x == x` must be `true` for every value `x` of that type. \n",
|
||
"\n",
|
||
"In practice, almost every type that implements `PartialEq` should implement `Eq` as well; `f32` and `f64` are the only types in the standard library that are `PartialEq` but not `Eq`.\n",
|
||
"\n",
|
||
"The standard library defines `Eq` as an extension of `PartialEq`, adding no new methods:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" trait Eq : PartialEq<Self> {}\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Implementing `Eq` for `Complex<T>` is straightforward:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"impl<T: Eq> Eq for Complex<T> {}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"We could also derive `Eq` for generic types that implement `PartialEq`:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n",
|
||
"struct Value<T> {\n",
|
||
" value: T\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Derived implementations on a generic type may depend on the type parameters. With the derive attribute, `Value<i32>` would implement `Eq`, because `i32` does, but `Value<f32>` would implement only `PartialEq`, since `f32` doesn’t implement `Eq`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### PartialOrd"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Any comparison for `f64` or `f32` involving `NaN` will return `false`:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 20,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"assert_eq!(0.0/0.0 < 0.0/0.0, false);\n",
|
||
"assert_eq!(0.0/0.0 > 0.0/0.0, false);\n",
|
||
"assert_eq!(0.0/0.0 <= 0.0/0.0, false);\n",
|
||
"assert_eq!(0.0/0.0 >= 0.0/0.0, false);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 21,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" enum Ordering {\n",
|
||
" Less,\n",
|
||
" Equal,\n",
|
||
" Greater,\n",
|
||
" }\n",
|
||
"\n",
|
||
" trait PartialOrd<RHS=Self>: PartialEq<RHS> \n",
|
||
" where \n",
|
||
" RHS: ?Sized \n",
|
||
" {\n",
|
||
" fn partial_cmp(&self, other: &RHS) -> Option<Ordering>;\n",
|
||
" \n",
|
||
" fn lt(&self, other: &RHS) -> bool { \n",
|
||
" match self.partial_cmp(other) {\n",
|
||
" Some(Ordering::Less) => true,\n",
|
||
" _ => false,\n",
|
||
" }\n",
|
||
" }\n",
|
||
" \n",
|
||
" fn le(&self, other: &RHS) -> bool { \n",
|
||
" match self.partial_cmp(other) {\n",
|
||
" Some(Ordering::Less) | Some(Ordering::Equal) => true,\n",
|
||
" _ => false,\n",
|
||
" }\n",
|
||
" }\n",
|
||
" \n",
|
||
" fn gt(&self, other: &RHS) -> bool { \n",
|
||
" match self.partial_cmp(other) {\n",
|
||
" Some(Ordering::Greater) => true,\n",
|
||
" _ => false,\n",
|
||
" }\n",
|
||
" }\n",
|
||
" \n",
|
||
" fn ge(&self, other: &RHS) -> bool { \n",
|
||
" match self.partial_cmp(other) {\n",
|
||
" Some(Ordering::Greater) | Some(Ordering::Equal) => true,\n",
|
||
" _ => false,\n",
|
||
" }\n",
|
||
" }\n",
|
||
" }\n",
|
||
"\n",
|
||
" // trait for types that can be totally ordered\n",
|
||
" trait Ord: Eq + PartialOrd<Self> {\n",
|
||
" fn cmp(&self, other: &Self) -> Ordering;\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 22,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"#[derive(PartialEq, Eq, Debug)]\n",
|
||
"struct Interval<T> {\n",
|
||
" lower: T, // inclusive\n",
|
||
" upper: T // exclusive \n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::cmp::{Ordering, PartialOrd};\n",
|
||
"\n",
|
||
"impl<T: PartialOrd> PartialOrd for Interval<T> {\n",
|
||
" fn partial_cmp(&self, other: &Interval<T>) -> Option<Ordering> {\n",
|
||
" if self == other {\n",
|
||
" Some(Ordering::Equal)\n",
|
||
" } else if self.lower >= other.upper {\n",
|
||
" Some(Ordering::Greater)\n",
|
||
" } else if self.upper <= other.lower {\n",
|
||
" Some(Ordering::Less)\n",
|
||
" } else {\n",
|
||
" None\n",
|
||
" } \n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"assert!(Interval { lower: 10, upper: 20 } < Interval { lower: 20, upper: 40 });\n",
|
||
"assert!(Interval { lower: 7, upper: 8 } >= Interval { lower: 0, upper: 1 });\n",
|
||
"assert!(Interval { lower: 7, upper: 8 } <= Interval { lower: 7, upper: 8 });\n",
|
||
"\n",
|
||
"// Overlapping intervals aren't ordered with respect to each other.\n",
|
||
"let left = Interval { lower: 10, upper: 30 };\n",
|
||
"let right = Interval { lower: 20, upper: 40 };\n",
|
||
"assert!(!(left < right));\n",
|
||
"assert!(!(left >= right));"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"#### PartialOrd & f64\n",
|
||
"\n",
|
||
"Floating-point numbers have a `PartialOrd` implementation, because `NaN` is neither greater than, less than, nor equal to any other value."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 25,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"None"
|
||
]
|
||
},
|
||
"execution_count": 25,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"3.14.partial_cmp(&f64::NAN)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"None"
|
||
]
|
||
},
|
||
"execution_count": 26,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"f64::NAN.partial_cmp(&f64::NAN)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Indexing\n",
|
||
"\n",
|
||
"* In order to use indexing expression `obj[i]` for a given type we have to implement `Index` (and optionally `IndexMut`) trait:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" \n",
|
||
" trait Index<Idx> {\n",
|
||
" type Output: ?Sized;\n",
|
||
" fn index(&self, index: Idx) -> &Self::Output;\n",
|
||
" }\n",
|
||
"\n",
|
||
" trait IndexMut<Idx>: Index<Idx> {\n",
|
||
" fn index_mut(&mut self, index: Idx) -> &mut Self::Output;\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 28,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"struct Image<P> {\n",
|
||
" width: usize,\n",
|
||
" pixels: Vec<P>,\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl<P: Default + Copy> Image<P> {\n",
|
||
"\n",
|
||
" /// Create a new image with the given dimensions.\n",
|
||
" fn new(width: usize, height: usize) -> Self {\n",
|
||
" Image {\n",
|
||
" width: width,\n",
|
||
" pixels: vec![P::default(); width * height],\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl<P> std::ops::Index<usize> for Image<P> {\n",
|
||
" type Output = [P];\n",
|
||
" \n",
|
||
" fn index(&self, row: usize) -> &[P] {\n",
|
||
" let start = row * self.width;\n",
|
||
" &self.pixels[start..start + self.width]\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl<P> std::ops::IndexMut<usize> for Image<P> {\n",
|
||
" fn index_mut(&mut self, row: usize) -> &mut [P] {\n",
|
||
" let start = row * self.width;\n",
|
||
" &mut self.pixels[start..start + self.width]\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 29,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"let mut bitmap = Image::new(2, 2);\n",
|
||
"\n",
|
||
"bitmap[0][0] = 1;\n",
|
||
"bitmap[0][1] = 2;\n",
|
||
"\n",
|
||
"assert!(bitmap[0][0] == 1);\n",
|
||
"assert!(bitmap[0][1] == 2);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Sorting floats"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* Common `sort()` method from `std::slice::SliceExt` trait doesn't work for floats because of the `NaN` values:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 30,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "the trait bound `{float}: Ord` is not satisfied",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0277] Error:\u001b[0m the trait bound `{float}: Ord` is not satisfied",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_30:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249md\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m the trait `Ord` is not implemented for `{float}`",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"let mut data = vec![2.1, 3.14, 0.3, 0.271, 0.0, 5.34, 1.0, 0.0];\n",
|
||
"\n",
|
||
"data.sort();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* When we assume that `NaN` is not present in the slice, we can use `sort_by()` method from `std::slice::SliceExt` trait combined with `partial_cmp()` method from `std::cmp::PartialOrd` trait:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 31,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"[-inf, NaN, NaN, 0.0, 0.271, 0.3, 1.0, 2.1, 3.14, 3.141592653589793, 5.34, inf]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"let mut data = vec![2.1, 3.14, 0.3, 0.271, 0.0, std::f64::NAN, 5.34, 1.0, 0.0/0.0, std::f64::consts::PI, std::f64::INFINITY, std::f64::NEG_INFINITY];\n",
|
||
"\n",
|
||
"data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less));\n",
|
||
"\n",
|
||
"println!(\"{:?}\", data);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Total ordering for floats\n",
|
||
"\n",
|
||
"* Since Rust 1.64 we can use `f64::total_cmp()` method. It returns `Ordering` enum which can be used to sort floats.\n",
|
||
"\n",
|
||
"* The floating point values are totally ordered in the following sequence:\n",
|
||
"\n",
|
||
" * negative quiet NaN\n",
|
||
" * negative signaling NaN\n",
|
||
" * negative infinity\n",
|
||
" * negative numbers\n",
|
||
" * negative subnormal numbers\n",
|
||
" * negative zero\n",
|
||
" * positive zero\n",
|
||
" * positive subnormal numbers\n",
|
||
" * positive numbers\n",
|
||
" * positive infinity\n",
|
||
" * positive signaling NaN\n",
|
||
" * positive quiet NaN."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 32,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"Greater"
|
||
]
|
||
},
|
||
"execution_count": 32,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"f64::NAN.total_cmp(&std::f64::consts::PI)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 33,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"[-inf, 0.0, 0.271, 0.3, 1.0, 2.1, 3.14, 3.141592653589793, 5.34, inf, NaN, NaN]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"let mut data = vec![2.1, 3.14, 0.3, 0.271, 0.0, std::f64::NAN, 5.34, 1.0, 0.0/0.0, std::f64::consts::PI, std::f64::INFINITY, std::f64::NEG_INFINITY];\n",
|
||
"\n",
|
||
"// data.sort_by(|a, b| f64::total_cmp(a, b));\n",
|
||
"data.sort_by(f64::total_cmp);\n",
|
||
"\n",
|
||
"println!(\"{:?}\", data);"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Rust",
|
||
"language": "rust",
|
||
"name": "rust"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": "rust",
|
||
"file_extension": ".rs",
|
||
"mimetype": "text/rust",
|
||
"name": "Rust",
|
||
"pygment_lexer": "rust",
|
||
"version": ""
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 2
|
||
}
|