intrinsic Library of RUST
Intrinsics is built in by the compiler and provided to other modules. For inherent functions, there is no need to ask how to implement them. Just understand their functions and use them when needed
. The internal library of the memory part has been introduced in the memory library chapter above. This section gives a brief introduction to other parts
intrinsic atomic operation function
The atomic operation function ensures that the data operated in the function will not be operated by other instructions in the atomic operation function. Generally, these functions directly correspond to the atomic instructions of the CPU and are implemented by assembly. Atomic in the intrinsic library_ XXX and atomic_xxx_ Functions of type XXX are atomic operation functions. Atomic operation functions are mainly used for critical protection in concurrent programming, and are the basis of other critical protection mechanisms, such as Mutex, RWlock, etc.
Mathematical function and bit operation function
Various integer and floating-point mathematical functions. This part is placed in intrinsic s mainly because modern CPU s have a lot of support for floating-point computing. These mathematical functions are implemented by assembly language, which is more efficient, so it is necessary to implement them by compiler.
Intronic Instruction Optimization and debugging function
Assertion class: assert_ Function of type XXXX
Function stack: caller_location
Summary
Intrinsics library is a means to complete the cross CPU architecture from the compiler level. Intrinsics is usually encapsulated by the upper library. However, in operating system programming and framework programming, there will still be an inevitable need for contact.
Basic Trait of RUST standard library
The behavior definition and Trait are implemented directly for generics in RUST
For example:
//Implement borrow < T > trail for all types impl<T: ?Sized> Borrow<T> for T { fn borrow(&self) -> &T { self } } //Implement Receiver Trait for all reference types impl<T: ?Sized> Receiver for &T {} //Implement Receiver Trait for all mutable reference types impl<T: ?Sized> Receiver for &mut T {} //Implement clone trail for all native immutable pointer types impl<T: ?Sized> Clone for *const T { fn clone(&self) -> Self { *self } } //Implement clone trail for native variable pointer types impl<T: ?Sized> Clone for *mut T { fn clone(&self) -> Self { *self } } //Implement asref < [T] > trail for slice type [T] impl<T> AsRef<[T]> for [T] { fn as_ref(&self) -> &[T] { self } } // The behavior of the slice is the fragment of the specific implementation // [T;N] represents array and [T] represents slice. Basically, the input characters can'T be simplified any more. // The following is a unified behavior for all slice types. Slice types can be considered as references to native arrays in c++/java, impl<T> [T] { pub const fn len(&self) -> usize { unsafe { crate::ptr::PtrRepr { const_ptr: self }.components.metadata } } pub const fn is_empty(&self) -> bool { self.len() == 0 } //The following code shows the simplicity and comprehensiveness of the RUST code pub const fn first(&self) -> Option<&T> { if let [first, ..] = self { Some(first) } else { None } } pub const fn split_first(&self) -> Option<(&T, &[T])> { if let [first, tail @ ..] = self { Some((first, tail)) } else { None } } pub unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) { // Compared with C/C++/Java, the following code is a dream for programmers unsafe { (self.get_unchecked(..mid), self.get_unchecked(mid..)) } } ... ... } //Native pointers are fragments of concrete event behavior impl<T: ?Sized> *const T { pub const fn is_null(self) -> bool { (self as *const u8).guaranteed_eq(null()) } /// Casts to a pointer of another type. pub const fn cast<U>(self) -> *const U { self as _ } pub unsafe fn as_ref<'a>(self) -> Option<&'a T> { if self.is_null() { None } else { unsafe { Some(&*self) } } } ... ... } //Native array pointer concrete behavior implementation fragment. Note that the native array pointer is also a native pointer, //The function with the same name of * const T should not be implemented here. impl<T> *const [T] { pub const fn len(self) -> usize { metadata(self) } pub const fn as_ptr(self) -> *const T { self as *const T } ... ... } // Implement partialeq < & b > trail for & A, if a implements partialeq < b > impl<A: ?Sized, B: ?Sized> PartialEq<&B> for &A where A: PartialEq<B>, { fn eq(&self, other: &&B) -> bool { PartialEq::eq(*self, *other) } fn ne(&self, other: &&B) -> bool { PartialEq::ne(*self, *other) } }
When implementing behaviors or traits, you can implement specific behaviors and traits for restricted generics. The restrictions of generics include: unlimited, native pointer (variable / immutable) type of generics, reference (variable / immutable) type of generics, array type of generics, slice type of generics, slice reference (variable / immutable) type of generics, tuple (variable / immutable) type of generics, function type, trail restriction, etc.
When you implement behavior and Trait for a type structure that has generics, you can also implement specific behavior and Trait for restricted generics.
Compiler built-in trail code analysis
Generally, there are not many built-in trail codes. Most of the contents in the codes are the reference documents of trail. This section mainly focuses on some less involved contents in the official tutorials and documents.
//Send Trait pub unsafe auto trait Send { // empty. } //Send trail is not supported for native pointers impl<T: ?Sized> !Send for *const T {} impl<T: ?Sized> !Send for *mut T {} mod impls { // The type that implements Sync is immutable, and the reference supports Send unsafe impl<T: Sync + ?Sized> Send for &T {} // The type variable reference of the type that implements Send supports Send unsafe impl<T: Send + ?Sized> Send for &mut T {} } // Sync Trait pub unsafe auto trait Sync { // Empty } //Sync trail is not supported for native pointers impl<T: ?Sized> !Sync for *const T {} impl<T: ?Sized> !Sync for *mut T {} pub trait Sized { // Empty. } //If a Sized type is to be converted to a dynamic size type, it must implement unsize train //For example, [T;N] implements unsize < [t] > pub trait Unsize<T: ?Sized> { // Empty. } //It is mainly used for pattern matching. If a structure implements PartialEq, the Trait will be implemented automatically. //It is speculated that this structure should be memory bit comparison pub trait StructuralPartialEq { // Empty. } //It is mainly used for pattern matching. If a structure implements Eq, the Trait will be implemented automatically. pub trait StructuralEq { // Empty. } //Copy, omitted pub trait Copy: Clone { // Empty. } //Unified implementation of native type support for copy trail mod copy_impls { use super::Copy; macro_rules! impl_copy { ($($t:ty)*) => { $( impl Copy for $t {} )* } } impl_copy! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 bool char } impl Copy for ! {} impl<T: ?Sized> Copy for *const T {} impl<T: ?Sized> Copy for *mut T {} //&Mut t does not support Copy to ensure the borrowing rules of RUST impl<T: ?Sized> Copy for &T {} } //Phantom data is used to make a mark in the type structure, marking some type attributes that need but do not have to have entities //For example, phantom data, please refer to the standard library for details. //PhantomData is a unit structure. The variable name of the unit structure is the type name of the unit structure. //Therefore, you can directly use phantom data when using it, and the compiler will automatically bring in the type instantiation information of generic types pub struct PhantomData<T: ?Sized>;
ops operator train code analysis
In RUST, all operation symbols can be overloaded.
A little rule
In the overloaded function, if the overloaded symbol appears, the compiler uses the specified default operation. For example:
impl const BitAnd for u8 { type Output = u8; //The & symbol inside the following function will no longer cause overloading, which is the default bitwise and operation of the compiler. fn bitand(self, rhs: u8) -> u8 { self & u8 } }
Mathematical operator train
Easy to understand, slightly
Bitwise operator train
Easy to understand, slightly
Relational operator train
The code and analysis are as follows
//Partial equality of type structures is allowed pub trait PartialEq<Rhs: ?Sized = Self> { ///"= =" overload behavior fn eq(&self, other: &Rhs) -> bool; /// `!=` Overload behavior fn ne(&self, other: &Rhs) -> bool { !self.eq(other) } } /// Derive macro generating an impl of the trait `PartialEq`. ///Procedure macros that implement the Derive property pub macro PartialEq($item:item) { /* compiler built-in */ } //Each attribute of a structure must be equal pub trait Eq: PartialEq<Self> { fn assert_receiver_is_total_eq(&self) {} } ///Procedure macros that implement the Derive property pub macro Eq($item:item) { /* compiler built-in */ } //A structure used to represent the result of a relationship #[derive(Clone, Copy, PartialEq, Debug, Hash)] #[repr(i8)] pub enum Ordering { /// An ordering where a compared value is less than another. Less = -1, /// An ordering where a compared value is equal to another. Equal = 0, /// An ordering where a compared value is greater than another. Greater = 1, } impl Ordering { pub const fn is_eq(self) -> bool { matches!(self, Equal) } //The implementation of the following function body is omitted pub const fn is_ne(self) -> bool pub const fn is_lt(self) -> bool pub const fn is_gt(self) -> bool pub const fn is_le(self) -> bool pub const fn is_ge(self) -> bool //Reverse operation pub const fn reverse(self) -> Ordering { match self { Less => Greater, Equal => Equal, Greater => Less, } } //Used to simplify code and better support functional programming //give an example: // let x: (i64, i64, i64) = (1, 2, 7); // let y: (i64, i64, i64) = (1, 5, 3); // let result = x.0.cmp(&y.0).then(x.1.cmp(&y.1)).then(x.2.cmp(&y.2)); pub const fn then(self, other: Ordering) -> Ordering { match self { Equal => other, _ => self, } } //Used to simplify code implementation and support functional programming pub fn then_with<F: FnOnce() -> Ordering>(self, f: F) -> Ordering { match self { Equal => f(), _ => self, } } } // "<" ">" "> =" "< =" "operator overloaded structure pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> { fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>; // "<" operator overload fn lt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Less)) } //"< =" operator overload fn le(&self, other: &Rhs) -> bool { // Pattern `Some(Less | Eq)` optimizes worse than negating `None | Some(Greater)`. !matches!(self.partial_cmp(other), None | Some(Greater)) } //">" operator overload fn gt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater)) } //"> =" operator overload fn ge(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater | Equal)) } } //Process macro to implement Derive pub macro PartialOrd($item:item) { /* compiler built-in */ } //Use the input closure comparison function to obtain the larger of the two values pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { match compare(&v1, &v2) { Ordering::Less | Ordering::Equal => v2, Ordering::Greater => v1, } } //Use the input closure comparison function to obtain the small one of the two values pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { match compare(&v1, &v2) { Ordering::Less | Ordering::Equal => v1, Ordering::Greater => v2, } } //The following code is easy to understand and omitted pub trait Ord: Eq + PartialOrd<Self> { fn cmp(&self, other: &Self) -> Ordering; fn max(self, other: Self) -> Self where Self: Sized, { max_by(self, other, Ord::cmp) } fn min(self, other: Self) -> Self where Self: Sized, { min_by(self, other, Ord::cmp) } fn clamp(self, min: Self, max: Self) -> Self where Self: Sized, { assert!(min <= max); if self < min { min } else if self > max { max } else { self } } } //Implement Drive property procedure macro pub macro Ord($item:item) { /* compiler built-in */ } pub fn min<T: Ord>(v1: T, v2: T) -> T { v1.min(v2) } pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) } pub fn max<T: Ord>(v1: T, v2: T) -> T { v1.max(v2) } pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) } //For types that implement PartialOrd, implementing the inversion of an Ord is a //Design pattern example of adapter pub struct Reverse<T>(pub T); impl<T: PartialOrd> PartialOrd for Reverse<T> { fn partial_cmp(&self, other: &Reverse<T>) -> Option<Ordering> { other.0.partial_cmp(&self.0) } fn lt(&self, other: &Self) -> bool { other.0 < self.0 } ... ... } impl<T: Ord> Ord for Reverse<T> { fn cmp(&self, other: &Reverse<T>) -> Ordering { other.0.cmp(&self.0) } ... } impl<T: Clone> Clone for Reverse<T> { fn clone(&self) -> Reverse<T> { Reverse(self.0.clone()) } fn clone_from(&mut self, other: &Self) { self.0.clone_from(&other.0) } } // Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types mod impls { use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::hint::unreachable_unchecked; //Implementation of PartialEq on native types macro_rules! partial_eq_impl { ($($t:ty)*) => ($( impl PartialEq for $t { fn eq(&self, other: &$t) -> bool { (*self) == (*other) } fn ne(&self, other: &$t) -> bool { (*self) != (*other) } } )*) } impl PartialEq for () { fn eq(&self, _other: &()) -> bool { true } fn ne(&self, _other: &()) -> bool { false } } partial_eq_impl! { bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } // Eq, partialord, implementation of ord on native types, omitted ... ... impl PartialEq for ! { fn eq(&self, _: &!) -> bool { *self } } impl Eq for ! {} impl PartialOrd for ! { fn partial_cmp(&self, _: &!) -> Option<Ordering> { *self } } impl Ord for ! { fn cmp(&self, _: &!) -> Ordering { *self } } //After a realizes partialeq < b > and partialord < b >, partialeq < & b > and partialord < & b > are realized for & A impl<A: ?Sized, B: ?Sized> PartialEq<&B> for &A where A: PartialEq<B>, { fn eq(&self, other: &&B) -> bool { //Pay attention to this calling method. Self. Cannot be used at this time EQ call. //The eq method parameter is a reference PartialEq::eq(*self, *other) } fn ne(&self, other: &&B) -> bool { PartialEq::ne(*self, *other) } } impl<A: ?Sized, B: ?Sized> PartialOrd<&B> for &A where A: PartialOrd<B>, { fn partial_cmp(&self, other: &&B) -> Option<Ordering> { PartialOrd::partial_cmp(*self, *other) } fn lt(&self, other: &&B) -> bool { PartialOrd::lt(*self, *other) } ... ... } //If a implements ord trail, implement ord trail for & A impl<A: ?Sized> Ord for &A where A: Ord, { fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(*self, *other) } } impl<A: ?Sized> Eq for &A where A: Eq {} impl<A: ?Sized, B: ?Sized> PartialEq<&mut B> for &mut A where A: PartialEq<B>, { fn eq(&self, other: &&mut B) -> bool { PartialEq::eq(*self, *other) } fn ne(&self, other: &&mut B) -> bool { PartialEq::ne(*self, *other) } } impl<A: ?Sized, B: ?Sized> PartialOrd<&mut B> for &mut A where A: PartialOrd<B>, { fn partial_cmp(&self, other: &&mut B) -> Option<Ordering> { PartialOrd::partial_cmp(*self, *other) } fn lt(&self, other: &&mut B) -> bool { PartialOrd::lt(*self, *other) } ... ... } impl<A: ?Sized> Ord for &mut A where A: Ord, { fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(*self, *other) } } impl<A: ?Sized> Eq for &mut A where A: Eq {} impl<A: ?Sized, B: ?Sized> PartialEq<&mut B> for &A where A: PartialEq<B>, { fn eq(&self, other: &&mut B) -> bool { PartialEq::eq(*self, *other) } fn ne(&self, other: &&mut B) -> bool { PartialEq::ne(*self, *other) } } impl<A: ?Sized, B: ?Sized> PartialEq<&B> for &mut A where A: PartialEq<B>, { fn eq(&self, other: &&B) -> bool { PartialEq::eq(*self, *other) } fn ne(&self, other: &&B) -> bool { PartialEq::ne(*self, *other) } } }
The operator overloading of relational operation is more complex than other mathematical operators and bit cloud algorithms, so the code is relatively complete. But its logic is still relatively simple.
? Operator train code analysis
? Operation is a foundation of functional programming supported by RUST. Even if it is not used for functional programming, the code can be greatly simplified.
For a function, when a type implements try trail. What can I do for this type? Operation simplification code.
Try trail is also an implementation of try... catch in RUST, but it is more simplified in the form of code. In addition, because it can return specific types, this implementation method is not limited to handling exceptions, but can be extended to other similar scenarios. In terms of code, you can also complete exception return processing through the behavior of return type, which is more flexible and makes the whole code logic more natural and simple.
Try train is defined as follows:
pub trait Try: FromResidual { /// The type of the value produced by `?` when *not* short-circuiting. /// ? The return value type of the normal end of the operator type Output; /// The type of the value passed to [`FromResidual::from_residual`] /// as part of `?` when short-circuiting. /// ? The value type returned by the operator in advance, which will be explained by examples later type Residual; /// Constructs the type from its `Output` type. ///Get the value of the original type from the return value type of Self::Output /// This should be implemented consistently with the `branch` method /// such that applying the `?` operator will get back the original value: ///This function must comply with the principles of the following code, /// `Try::from_output(x).branch() --> ControlFlow::Continue(x)`. ///Example: /// ``` /// #![feature(try_trait_v2)] /// use std::ops::Try; /// /// assert_eq!(<Result<_, String> as Try>::from_output(3), Ok(3)); /// assert_eq!(<Option<_> as Try>::from_output(4), Some(4)); /// assert_eq!( /// <std::ops::ControlFlow<String, _> as Try>::from_output(5), /// std::ops::ControlFlow::Continue(5), /// ); fn from_output(output: Self::Output) -> Self; ///The branch function will return ControlFlow, which determines whether the process continues or returns in advance ///Example: /// ```p /// #![feature(try_trait_v2)] /// use std::ops::{ControlFlow, Try}; /// /// assert_eq!(Ok::<_, String>(3).branch(), ControlFlow::Continue(3)); /// assert_eq!(Err::<String, _>(3).branch(), ControlFlow::Break(Err(3))); /// /// assert_eq!(Some(3).branch(), ControlFlow::Continue(3)); /// assert_eq!(None::<String>.branch(), ControlFlow::Break(None)); /// /// assert_eq!(ControlFlow::<String, _>::Continue(3).branch(), ControlFlow::Continue(3)); /// assert_eq!( /// ControlFlow::<_, String>::Break(3).branch(), /// ControlFlow::Break(ControlFlow::Break(3)), /// ); fn branch(self) -> ControlFlow<Self::Residual, Self::Output>; } pub trait FromResidual<R = <Self as Try>::Residual> { ///This function gets the original value from the value returned in advance /// /// This should be implemented consistently with the `branch` method such /// that applying the `?` operator will get back an equivalent residual: ///This function must conform to the principles of the following code /// `FromResidual::from_residual(r).branch() --> ControlFlow::Break(r)`. ///Example: /// ``` /// #![feature(try_trait_v2)] /// use std::ops::{ControlFlow, FromResidual}; /// /// assert_eq!(Result::<String, i64>::from_residual(Err(3_u8)), Err(3)); /// assert_eq!(Option::<String>::from_residual(None), None); /// assert_eq!( /// ControlFlow::<_, String>::from_residual(ControlFlow::Break(5)), /// ControlFlow::Break(5), /// ); /// ``` fn from_residual(residual: R) -> Self; }
Try trail, right? Examples of operation support are as follows:
//no need? Operation code pub fn simple_try_fold_3<A, T, R: Try<Output = A>>( iter: impl Iterator<Item = T>, mut accum: A, mut f: impl FnMut(A, T) -> R, ) -> R { for x in iter { let cf = f(accum, x).branch(); match cf { ControlFlow::Continue(a) => accum = a, ControlFlow::Break(r) => return R::from_residual(r), } } R::from_output(accum) } // use? Operation code: fn simple_try_fold<A, T, R: Try<Output = A>>( iter: impl Iterator<Item = T>, mut accum: A, mut f: impl FnMut(A, T) -> R, ) -> R { for x in iter { accum = f(accum, x)?; } R::from_output(accum) }
From the above, it can be inferred that T? Indicates the following code
match((T as Try).branch()) { ControlFlow::Continue(a) => a, ControlFlow::Break(r) => return (T as Try)::from_residual(r), }
The ControlFlow type code is as follows, which is mainly used to indicate the control process. Logically, it can be analogized to continue. The break keyword code is as follows:
pub enum ControlFlow<B, C = ()> { /// Move on to the next phase of the operation as normal. Continue(C), /// Exit the operation without running subsequent phases. Break(B), // Yes, the order of the variants doesn't match the type parameters. // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>` // is a no-op conversion in the `Try` implementation. } impl<B, C> ops::Try for ControlFlow<B, C> { type Output = C; // Convert:: invalid means that the type will not be used, which can be considered as ignoring the type, //For Residual, only ControlFlow::Break(B) will be used, so it is explicitly stated that this type will not be used with infinite type Residual = ControlFlow<B, convert::Infallible>; fn from_output(output: Self::Output) -> Self { ControlFlow::Continue(output) } fn branch(self) -> ControlFlow<Self::Residual, Self::Output> { match self { ControlFlow::Continue(c) => ControlFlow::Continue(c), ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)), } } impl<B, C> ops::FromResidual for ControlFlow<B, C> { // Impossible means that the type will not be used fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self { match residual { ControlFlow::Break(b) => ControlFlow::Break(b), } } }
Try train implementation of Option:
impl<T> ops::Try for Option<T> { type Output = T; // Infallible is an error type, but the error will never occur. Here, you need to return None, // So you need Option, but you only need None. There will be no other, so we use invisible type Residual = Option<convert::Infallible>; fn from_output(output: Self::Output) -> Self { Some(output) } fn branch(self) -> ControlFlow<Self::Residual, Self::Output> { match self { Some(v) => ControlFlow::Continue(v), None => ControlFlow::Break(None), } } } impl<T> const ops::FromResidual for Option<T> { #[inline] fn from_residual(residual: Option<convert::Infallible>) -> Self { match residual { None => None, } } }
An iterator:: try_ Implementation of fold():
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R where Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Output = B>, { let mut accum = init; while let Some(x) = self.next() { accum = f(accum, x)?; } //try keywordconverts accum to a variable of type R try { accum } }
Summary
With try train, programmers can implement custom types?, Provide a powerful means of functional programming, simplify the code and improve the understanding of the code.
Range operator code analysis
Range is the form of symbol..., start... End, start...,... End,... = end, start... = end
Range related boundary structure Bound
Source code:
pub enum Bound<T> { /// An inclusive bound. ///Boundary includes Included(T), /// An exclusive bound. ///Boundary does not include Excluded(T), /// An infinite endpoint. Indicates that there is no bound in this direction. ///Boundary does not exist Unbounded, }
The value of the Include boundary contains, the value of the excluded boundary does not contain, and the value of the Unbounded boundary does not exist
RangeFull
.. Data structure.
Range<Idx>
start.. end data structure
RangeFrom<Idx>
start.. Data structure of
RangeTo<Idx>
.. end
RangeInclusive<Idx>
start..=end data structure
RangeToInclusive<Idx>
.. = end data structure
RangeBounds<T: ?Sized>
Trail implemented by all ranges.
pub trait RangeBounds<T: ?Sized> { ///Gets the starting value of the range /// ///Examples /// # fn main() { /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// /// assert_eq!((..10).start_bound(), Unbounded); /// assert_eq!((3..10).start_bound(), Included(&3)); /// # } /// ``` fn start_bound(&self) -> Bound<&T>; ///Gets the end value of the range ///Examples /// # fn main() { /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// /// assert_eq!((3..).end_bound(), Unbounded); /// assert_eq!((3..10).end_bound(), Excluded(&10)); /// # } /// ``` fn end_bound(&self) -> Bound<&T>; ///Whether the range includes a value ///Examples /// assert!( (3..5).contains(&4)); /// assert!(!(3..5).contains(&2)); /// /// assert!( (0.0..1.0).contains(&0.5)); /// assert!(!(0.0..1.0).contains(&f32::NAN)); /// assert!(!(0.0..f32::NAN).contains(&0.5)); /// assert!(!(f32::NAN..1.0).contains(&0.5)); fn contains<U>(&self, item: &U) -> bool where T: PartialOrd<U>, U: ?Sized + PartialOrd<T>, { (match self.start_bound() { Included(start) => start <= item, Excluded(start) => start < item, Unbounded => true, }) && (match self.end_bound() { Included(end) => item <= end, Excluded(end) => item < end, Unbounded => true, }) } }
RangeBounds implements RangeFull, rangeTo, rangeinclusive, rangetoinclusive, rangefrom and range structures. At the same time, the tuple of (Bound, Bound) is implemented.
Range independence
In fact, the Range operator alone is meaningless. It is mainly used in combination with Index operator or Iterator. In the subsequent Index operator and Iterator, we will study how Range is combined with them.
Summary
The Range type based on generics provides a very good syntax means. As long as a type supports sorting, you can define a Range type based on this type. Combined with Index and Iterator, it will efficiently realize the code with great impact.
Code analysis of Index operator of RUST
Array subscript [] is overloaded by index and indexmut. Array subscript overloading makes the program more readable. Two trails are defined as follows:
pub trait Index<Idx: ?Sized> { /// The returned type after indexing. type Output: ?Sized; ///If the parameter passed in exceeds the memory limit, panic will be triggered immediately fn index(&self, index: Idx) -> &Self::Output; } pub trait IndexMut<Idx: ?Sized>: Index<Idx> { fn index_mut(&mut self, index: Idx) -> &mut Self::Output; }
Index implementation of slice data structure [T]
impl<T, I> ops::Index<I> for [T] where I: SliceIndex<[T]>, { type Output = I::Output; fn index(&self, index: I) -> &I::Output { index.index(self) } } impl<T, I> ops::IndexMut<I> for [T] where I: SliceIndex<[T]>, { fn index_mut(&mut self, index: I) -> &mut I::Output { index.index_mut(self) } } Need to rely on SliceIndex Trait realization[T]of ops::Index. This design mainly needs to be used SliceIndex Namely realize usize The subscript takes out a single element and implements it again Range Subscript extractor slice. The following is an example RUST Programming skills mod private_slice_index { use super::ops; pub trait Sealed {} impl Sealed for usize {} impl Sealed for ops::Range<usize> {} impl Sealed for ops::RangeTo<usize> {} impl Sealed for ops::RangeFrom<usize> {} impl Sealed for ops::RangeFull {} impl Sealed for ops::RangeInclusive<usize> {} impl Sealed for ops::RangeToInclusive<usize> {} impl Sealed for (ops::Bound<usize>, ops::Bound<usize>) {} } pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed { /// The output type returned by methods. type Output: ?Sized; /// Returns a shared reference to the output at this location, if in /// bounds. fn get(self, slice: &T) -> Option<&Self::Output>; /// Returns a mutable reference to the output at this location, if in /// bounds. fn get_mut(self, slice: &mut T) -> Option<&mut Self::Output>; /// Returns a shared reference to the output at this location, without /// performing any bounds checking. /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting reference is not used. /// /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output; /// Returns a mutable reference to the output at this location, without /// performing any bounds checking. /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting reference is not used. /// /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html unsafe fn get_unchecked_mut(self, slice: *mut T) -> *mut Self::Output; /// Returns a shared reference to the output at this location, panicking /// if out of bounds. fn index(self, slice: &T) -> &Self::Output; /// Returns a mutable reference to the output at this location, panicking /// if out of bounds. fn index_mut(self, slice: &mut T) -> &mut Self::Output; }
pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed
SliceIndex inherits Sealed, that is, the structure implementing SliceIndex must implement Sealed. But the module to which Sealed belongs is private_slice_index is not public, so SliceIndex cannot be inherited in essence and can only be used inside the file. This is a programming skill of RUST, so that even if trail is public, it can not be implemented elsewhere.
unsafe impl<T> SliceIndex<[T]> for usize { type Output = T; fn get(self, slice: &[T]) -> Option<&T> { // SAFETY: `self` is checked to be in bounds. if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None } } fn get_mut(self, slice: &mut [T]) -> Option<&mut T> { // SAFETY: `self` is checked to be in bounds. if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None } } unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. unsafe { slice.as_ptr().add(self) } } unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { // SAFETY: see comments for `get_unchecked` above. unsafe { slice.as_mut_ptr().add(self) } } fn index(self, slice: &[T]) -> &T { // N. B. use intrinsic indexing. get should be used here, but it should be built-in support of the compiler. For efficiency, the built-in array subscript is directly used. &(*slice)[self] } fn index_mut(self, slice: &mut [T]) -> &mut T { // N.B., use intrinsic indexing &mut (*slice)[self] } }
The above is the underlying implementation of ops::Index and ops::IndexMut for [T] to take out a single element with an unsigned number as a subscript. ptr must be applied to take out a single element from slice. RUST seems to have the following characteristics. If the symbol overloading function uses the symbol again, it will no longer overload the symbol and calculate directly
unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> { type Output = [T]; #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { if self.start > self.end || self.end > slice.len() { None } else { // SAFETY: `self` is checked to be valid and in bounds above. unsafe { Some(&*self.get_unchecked(slice)) } } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { if self.start > self.end || self.end > slice.len() { None } else { // SAFETY: `self` is checked to be valid and in bounds above. unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } } #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { // SAFETY: see comments for `get_unchecked` above. unsafe { ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start) } } #[inline] fn index(self, slice: &[T]) -> &[T] { if self.start > self.end { slice_index_order_fail(self.start, self.end); } else if self.end > slice.len() { slice_end_index_len_fail(self.end, slice.len()); } // SAFETY: `self` is checked to be valid and in bounds above. unsafe { &*self.get_unchecked(slice) } } #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { if self.start > self.end { slice_index_order_fail(self.start, self.end); } else if self.end > slice.len() { slice_end_index_len_fail(self.end, slice.len()); } // SAFETY: `self` is checked to be valid and in bounds above. unsafe { &mut *self.get_unchecked_mut(slice) } } }
The above is an implementation of taking the sub slice from the slice with Range. The core is still PTR:: slice_ from_ raw_ Operation of parts. Others, such as RangeTo, are similar to Range.
ops::Index implementation of array data structure [T;N]
#[stable(feature = "index_trait_on_arrays", since = "1.50.0")] impl<T, I, const N: usize> Index<I> for [T; N] where [T]: Index<I>, { type Output = <[T] as Index<I>>::Output; #[inline] fn index(&self, index: I) -> &Self::Output { Index::index(self as &[T], index) } } #[stable(feature = "index_trait_on_arrays", since = "1.50.0")] impl<T, I, const N: usize> IndexMut<I> for [T; N] where [T]: IndexMut<I>, { #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { IndexMut::index_mut(self as &mut [T], index) } }
Above, self as & [T] is to convert [T;N] into slice [T], so the Index of the array is the Index implementation of [T]