trainings/Rust_2026/rust-bs-2026-09-14/notebooks/12_operator_overloading.ipynb

1055 lines
26 KiB
Text
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"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
}