当前位置:网站首页>[rust notes] 09- special types and generics
[rust notes] 09- special types and generics
2022-07-03 08:35:00 【phial03】
09 - Special and generic
Rust The polymorphism of :
- Special type (trait)
- Generic (generic)
Special type :Rust Implementation of interfaces or abstract base classes .
The following is a special type of writing section
std::io::Write:trait Write { fn write(&mut self, buf: &[u8]) -> Result<usize>; fn flus(&mut self) -> Result<()>; fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... } ... }The built-in feature is Rust Language provides hooks for operator overloading and other features (hook).
Generic : Generic functions or types can be used with many different types of values .
/// Given two values , Take the smaller value fn min<T: Ord>(value1: T, value2: T) -> T { if value1 <= value2 { value1 } else { value2 } }<T: Ord>representativeminYou can use the implementationOrdAny type parameter of special type T;- Statement
T: OrdIt's called binding .
9.1 - Use special type
- A feature is a feature of any type that you can choose to support or not . Common features :
- Realization
std::io::WriteThe value of can be used to write bytes ; - Realization
std::iter::IteratorThe value of can produce a sequence of values ; - Realization
std::clone::CloneThe value of can clone itself in memory ; - Realization
std::fmt::DebugThe value of can be usedprintln!()Of{:?}Format specifiers print out .
- Realization
- Common standard libraries realize the above features :
std::fs::FileRealizedWriteSpecial type , Bytes can be written to local files .std::net::TcpStreamIs to write bytes to the network connection .Vec<u8>It's also implementedWrite. Each call on the byte vector.write(), You can append some data at the end of the vector .Range<i32>RealizedIteratorSpecial type , And slice 、 Hash table and other related atmospheric types also realize this feature .- except
TcpStreamThis is not only the type of data in memory , Most standard library types implementCloneSpecial type . - Most standard libraries support
DebugSpecial type .
- The feature itself must be in the scope .
- You can use special types to add new methods to any type .
CloneandIteratorMethod , The default is always in scope . They are part of the standard front-end modules ,Rust It will be automatically imported into all modules .- Standard front module , The essence is a special set .
9.1.1 - Special target
Rust There are two ways to write polymorphic code using stereotypes : Special targets and generic .
Rust It is not allowed to declare
WriteVariable of type : Because the size of variables must be known at compile time , To achieveWriteThe type of can be any size .Special target : Point to a special type ( Like the one below
writer) References to , It has the commonality of quotation : Point to a value , There is a life cycle , It can bemutOr shared references .let mut buf: Vec<u8> = vec![]; let writer: &mut Write = &mut buf;The difference between special goals :Rust The type of reference target is usually unknown at compile time .
9.1.2 - Memory layout of special target
The special target is a fat pointer , Contains a pointer to a value and a pointer to a table representing the value type . Each special target occupies two machine words .
Rust When necessary, it will automatically convert ordinary references to special targets .
let mut local_file = File::create("hello.txt")?; // &mut local_file The type is &mut File say_hello(&mut local_file)?; // say_hello The parameter type of is &mut Write
9.1.3 - Generic functions
as follows
say_hello()Generic functions , Receive a special target as a parameter .fn say_hello<W: Write>(out: &mut W) -> std::io::Result<()> { out.write_all(b"hello world\n")?; out.flush() }<W: Write>Is a type parameter (type parameter), It means thatWriteA certain type of special type .- The type parameter is usually a capital letter .
Generic functions can have multiple type parameters :
fn run_query<M: Mapper + Serialize, R: Reducer + Serialize>(data: &DataSet, map: M, reduce: R) -> Results { ... }The binding of the above code is relatively long , have access to
wherekeyword , Express the above code structure clearly :fn run_query<M, R>(data: &DataSet, map: M, reduce: R) -> Results where M: Mapper + Serialize, R: Reducer + Serialize { ... }Such type parameters
MandRIt is still announced in advance , But the binding is transferred to the later .whereClause also applies to generic structures 、 enumeration 、 Type aliases and methods , Wherever binding is allowed .
Generic functions can have both lifetime parameters and type parameters . Lifetime parameters are written in front :
fn nearest<'t, 'c, P>(target: &'t P, candidates: &'c [P]) -> &'c P where P: MeasureDistance { ... }Besides functions , Other features also support generics :
Generic structs
Generic enumeration
Generic methods , Whether or not the type that defines the method is generic .
impl PancakeStack { fn push<T: Topping>(&mut self, goop: T) -> PancakeResult<()> { ... } }Generic type alias
type PancakeResult<T> = Result<T, PancakeError>;Generic and special
binding 、
whereClause 、 Life cycle parameters, etc , It can be applied to all generic feature items mentioned above .
9.1.4 - Use scenarios
- Define a mixed type value , Put it all together , It is recommended to use feature targets .
- Using special targets can reduce the total amount of compiled code , Prevent code bloat (code bloat), Suitable for making microcontrollers .
- Advantages of generics over special targets :
- Speed : Eliminate the time of dynamic search ;
- Not all special models support special goals . For example, static methods are only valid for generics , No special objects are considered at all .
9.2 - Define and implement features
Define the special type : Name it and list the type signatures of the special methods :
trait Visible { fn draw(&self, canvas: &mut Canvas); fn hit_test(&self, x: i32, y: i32) -> bool; }Realize special , Use
impl TraitName for Typegrammar :impl Visible for Broom { fn draw(&self, canvas: &mut Canvas) { for y in self.y - self.height - 1 .. self.y { canvas.write_at(self.x, y, '|'); } canvas.write_at(self.x, self.y, 'M'); } fn hit_test(&self, x: i32, y: i32) -> bool { self.x == x && self.y - self.height - 1 <= y && y <= self.y } }Special type
implAs defined in , They are all special characteristics .If you want to add a support
Broom::draw()Auxiliary method of , Then in a separateimplDefine it in the block :impl Broom { fn broomstick_range(&self) -> Range<i32> { self.y - self.height - 1 .. self.y } } impl Visible for Broom { fn draw(&self, canvas: &mut Canvas) { for y in self.broomstick_range() { ... } ... } ... }
9.2.1 - The default method
Standard library
WriteThe definition of a feature includes awrite_allDefault implementation of :trait Write { fn write(&mut self, buf: &[u8]) -> Result<usize>; fn flush(&mut self) -> Result<()>; fn write_all(&mut self, buf: &[u8]) -> Result<()> { let mut bytes_written = 0; while bytes_written < buf.len() { bytes_written += self.write(&buf[bytes_written..])?; } Ok(()) } ... }writeandflushMethod is the basic method that every writer must implement ;- The writer can also realize
write_all, without , Then the above default implementation will be used .
The most default methods in the standard library are
IteratorSpecial type , It has a necessary method (.next()) And many other default methods .
9.2.2 - Special type and other people's type
Rust It is allowed to implement any special type on any type , As long as the relevant features or types are imported into the current package .
Whenever you want to add a method to any type , Can use special type to complete :
trait IsEmoji { fn is_emoji(&self) -> bool; } /// Implement for built-in character types IsEmoji Method , This type is called extended type impl IsEmoji for char { fn is_emoji(&self) -> bool { ... } } assert_eq!('$'.is_emoji(), false);You can use generics
implblock , Let a type family realize an extended special type at one time .use std::io::{ self, Write}; /// You can send HTML The value of defines the extended type trait WriteHtml { fn write_html(&mut self, html: &HtmlDocument) -> io::Result<()>; } /// In this way, any std::io::writer write in HTML 了 // For each implementation Write The type of W, Here for W Then realize the special type WriteHtml impl<W: Write> WriteHtml for W { fn write_html(&mut self, html: &HtmlDocument) -> io::Result<()> { ... } }serdeFor all related types, a.serialize()Method :use serde::Serialize; use serde_json; pub fn save_configuration(config: &HashMap<String, String>) -> std::io::Result<()> { // Create a JSON Serialization handler , Write data to file let writer = File::create(config_filename())?; let mut serializer = serde_json::Serializer::new(writer); // The rest of the task is left to serde Of .serialize() Method treatment config.serialize(&mut serializer)?; Ok(()) }Coherence rule (coherence rule): When realizing the special type , The related features or types must be new in the current package . Ensure the uniqueness of the feature implementation .
9.2.3 - Special type Self
Special type can be used
SelfKeyword as type .pub trait Clone { fn clone(&self) -> Self; ... }SelfAs return type :x.clone()The type ofxThe type of .
The following features have two implementations :
pub trait Spliceable { fn splice(&self, other: &Self) -> Self } // self and other The type of must be exactly the same impl Spliceable for CherryTree { fn splice(&self, other: &Self) -> Self { // here Self yes CherryTree Another name for ... } } impl Spliceable for Mammoth { fn splice(&self, other: &Self) -> Self { // here Self yes Mammoth Another name for ... } }Use
SelfThe special type of type cannot coexist with the special target :// error : Special type Spliceable Cannot be a reference target fn splice_anything(left: &Spliceable, right: &Spliceable) { let combo = left.splice(right); ... }- Use special targets , The type can only be determined at runtime ;
- At compile time ,Rust Can't judge
leftandrightIs it the same type
Special goals can only be achieved for the simplest special types . This special type can be used Java The interface , perhaps C++ Abstract base class implementation in .
Special advanced special , Cannot coexist with special targets .
How to design a goal friendly feature :
pub trait MegaSpliceable { fn splice(&self, other: &MegaSpliceable) -> Box<MegaSpliceable>; } // call .splice() When the method is used ,other The type of parameter does not have to follow self The type is exactly the same // As long as both types are MegaSpliceable You can pass the type check
9.2.4 - Sub type
You can declare a special type as an extension of another special type
// All with roles Creature Relevant codes , You can also use the from Visible Special method trait Creature: Visible { fn position(&self) -> (i32, i32); fn facing(&self) -> Direction; ... }All implementation
CreatureThe type of , Must also be achievedVisibleSpecial type :impl Visible for Broom { ... } impl Creature for Broom { ... }Subtypes are similar Java or C# Word interface for , It is a special type. You need to use several other expressions to extend the existing special type .
9.2.5 - Static methods
Unlike other object-oriented languages ,Rust Special types can contain static methods and constructors .
trait StringSet { /// Returns a new empty set fn new() -> Self; /// Returns a containing strings A collection of all strings in fn from_slice(strings: &[&str]) -> Self; /// Determines whether the current collection contains a specific value fn contains(&self, string: &str) -> bool; /// Add a string to the current collection fn add(&mut self, string: &str); }All implementation
StringSetSpecial type , Must achieve this 4 Correlation functionsThe first two functions do not receive
selfParameters , They act as constructors . In non generic code , These functions can be used::Syntax call , Just like calling other static methods :// Create two implementations StringSet A set of hypothetical types let set1 = SortedStringSet::new(); let set2 = HashedStringSet::new();In generic code , The type is variable :
/// return document Not in wordlis A collection of words in fn unknown_words<S: StringSet>(document: &Vec<String>, wordlist: &S) -> S { let mut unknowns = S::new(); for word in document { if !wordlist.contains(word) { unknowns.add(word); } } unknowns }
Special targets do not support static methods : If you want to use special targets
&StringSet, You have to modify the special type , Addwhere Self: Sizedbinding .trait StringSet { fn new() -> Self where Self: Sized; fn from_slice(strings: &[&str]) -> Self where Self: Sized; fn contains(&self, string: &str) -> bool; fn add(&mut self, string: &str); }- tell Rust, Special targets will be exempt from supporting this static method .
- Use this way
&StringSetSpecial target , You can call.contains()and.add()Method .
9.3 - Fully qualified method calls
Method is actually a special function :
"hello".to_string() // Equivalent to str::to_string("hello")to_stringIt's a standard.ToStringSpecial method , The above code is equivalent to :ToString::to_string("hello") <str as ToString>::to_string("hello")Above 4 Method calls , Do the same :
- In general ,
value.method()This form is commonly used ; - Other forms are called qualified method calls :
- You need to specify the type or special type associated with the method
- The last form with angle brackets , At the same time, the associated type is specified , And special type , Therefore, it is called fully qualified method call .
- In general ,
Fully qualified method calls , Specify exactly the method to use , The application scenarios are :
Two methods have the same name
outlaw.draw(); // error ,.draw() Method contains outlaw Method Visible::draw(&outlaw); HasPistol::draw(&outlaw);It is impossible to infer
selfType of parameterlet zero = 0; // Type not specified zeor.abs(); // error , Can't find abs Method i64::abs(zero); // SureTake the function itself as a value
let words: Vec<String> = line.split_whitespace() // produce &str Value iterator .map(<str as ToString>::to_string) // Sure .collect();Call special methods in macros .
Fully qualified syntax also applies to static methods .
9.4 - Define the special type of type relationship
- A stereotype is a set of methods that a type can implement .
- Special types can be used to describe the relationship between types . There are the following 3 Ways of planting :
9.4.1 - Association type : How iterators work
std::iter::IteratorA type that generates values by itself , Associate different iterator types .iterator : Objects that can traverse a series of values through it .
pub trait Iterator { type Item; // Association type (associated type) fn next(&mut self) -> Option<Self::Item>; ... }All implementation
IteratorThe type of , Must specify their own items (item) The type of .Achieve one
IteratorThe type of :// std::env Part of the code of the standard library module impl Iterator for Args { type Item = String; // Type declaration fn next(&mut self) -> Option<String> { ... } ... }
Generic code can use association types :
/// Iterate over an iterator , Store their values in a new variable fn collect_into_vector<I: Iterator>(iter: I) -> Vec<I::Item> { let mut results = Vec::new(); for value in iter { results.push(value); } results }Association types are also commonly used when special types need to cover definitions other than one method :
In the library of a thread pool
TaskSpecial type ( Represents a unit of work ), Can contain an associatedOutputSpecial type ;One
PatternSpecial type ( A way to represent a search string ), Can contain an associatedMatchtype , Represents all the information collected after the pattern matches the string :trait Patern { type Match; fn search(&self, string: &str) -> Option<Self::Match>; } /// You can search for specific characters in the string impl Pattern for char { /// Match( matching ) Represents the position of the character found when type Match = usize; fn search(&self, string: &str) -> Option<usize> { ... } }A library that operates relational databases can have one
DatabaseConnectionSpecial type , There can be transactions 、 The pointer 、 Initialization statements and other related functions .
9.4.2 - Generic and special : The principle of operator overloading
std::ops::MulThe type of special association that can participate in multiplication ./// Support * The type of operator implements the special type pub trait Mul<RHS> { // RHS:Right Hand Side Right hand side /// application * The type result returned after the operator type Output; /// * Operator corresponding method fn mul(self, rhs: RHS) -> Self::Output; }- expression
lhs * rhsyesMul::mul(lhs, rhs)Abbreviation
- expression
9.4.3 - Companion type :rand::random working principle
randIt contains a special type related to random number generatorrand::Rng, It also contains a special type related to the type that can be randomly generatedrand::Rand.- Companion type : Special type for collaborative work .
9.5 - Reverse engineering binding
Like the following code :
use std::ops::{ Add, Mul}; fn dot<N>(v1: &[N], v2: &[N]) -> N where N: Add<Output=N> + Mul<Output=N> + Default + Copy // Yes N The binding of { let mut total = N::default(); for i in 0..v1.len() { total = total + v1[i] * v2[i]; } total } #[test] fn test_dot() { assert_eq!(dot(&[1, 2, 3, 4], &[1, 1, 1, 1]), 10); assert_eq!(dot(&[53.0, 7.0], &[1.0, 5.0]), 88.0); }Yes
NThe binding of is reverse engineered , Let the compiler be the guide , Double check your workNumberThe special type contains all operators and methods .Advantages of reverse engineering binding :
- You can make generic code forward compatible .
- Can guide the trouble to be solved through the compiler error .
- It exists in both code and documentation .
See 《Rust Programming 》( Jim - Brandy 、 Jason, - By orendov , Translated by lisongfeng ) Chapter 11
Original address
边栏推荐
- KunlunBase MeetUP 等您来!
- Markdown directory generation
- MySQL 8
- Classes and objects
- Unity learning notes
- Abstract classes and interfaces
- Collection interface
- Unity interactive water ripple post-treatment
- 100 GIS practical application cases (78) - Multi compliance database design and data warehousing
- P1596 [USACO10OCT]Lake Counting S
猜你喜欢
![[cloud native] introduction and use of feign of microservices](/img/39/05cf7673155954c90e75a8a2eecd96.jpg)
[cloud native] introduction and use of feign of microservices

梯度下降法求解BP神经网络的简单Demo

Chocolate installation

UE4 source code reading_ Bone model and animation system_ Animation node

Use of ue5 QRcode plug-in

二进制转十进制,十进制转二进制

【Rust笔记】02-所有权

Base64编码简介
![[redis] redis persistent RDB vs AOF (source code)](/img/57/b6a86c49cedee31fc00dc5d1372023.jpg)
[redis] redis persistent RDB vs AOF (source code)

Unity learning notes
随机推荐
Exe file running window embedding QT window
Graphics_ Games101/202 learning notes
使用base64编码传图片
数据分析练习题
Conversion between golang JSON format and structure
Conversion between string and int types in golang
UE4 call DLL
Osgearth north arrow display
Gradle's method of dynamically modifying APK package name
php-fpm软件的安装+openresty高速缓存搭建
Vscode, idea, VIM development tool shortcut keys
Message queue for interprocess communication
Unity interactive water ripple post-treatment
【Rust 笔记】11-实用特型
Eating fruit
Kunlunbase meetup is waiting for you!
producer consumer problem
Clion toolchains are not configured configure disable profile problem solving
[concurrent programming] Table hopping and blocking queue
VIM learning notes from introduction to silk skating