Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the standard library workers commonly used in Rust?

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains what the standard library workers commonly used in Rust have, which friends who are interested may wish to have a look. The method introduced in this paper is simple, fast and practical. Next, let the editor take you to learn what the standard library workers commonly used in Rust have.

Prelude

We usually use the use statement to introduce the contents of other modules into the current module, but for some very commonly used tools in the standard library, writing use statements each time is too redundant, so the standard library provides a std::prelude module in which some of the most common tools are exported, and then the compiler automatically inserts a sentence for every crate written by the user:

Use std::prelude::*

In this way, some of the most common tools can be used directly without use. The tradeoff of the content contained in this std::prelude module requires careful consideration, because if it contains too much content, it will introduce a lot of tools that are not used, which is not beautiful. The content contained in the current std::prelude module is in std::prelude::v1.

Type conversion

In addition to the as keyword that can be used to convert between basic types, Rust also provides a lot of trait to implement conversion between custom types.

AsRef / AsMut

AsRef means that this type can get a shared reference of another type by calling the as_ref method. Similarly, AsMut gets a read-write reference of another type, which is defined as follows:

Pub trait AsRef {fn as_ref (& self)-> & T;} pub trait AsMut {fn as_mut (& mut self)-> & mut T;}

AsRef is ideal for use in generic code. For example, the following generic function accepts various types as long as it can be converted to & [U8]:

Fn iter_bytes (arg: & T) {for i in arg.as_ref () {println! ("{}", I);}} fn main () {let s = String::from ("this is a string"); let v = vec! [1,2,3]; let c = "hello"; iter_bytes (& s); iter_bytes (& v); iter_bytes (& c);} Borrow / BorrowMut

The design of Borrow and BorrowMut trait is similar to that of AsRef and AsMut:

Pub trait Borrow {fn borrow (& self)-> & Borrowed;} pub trait BorrowMut: Borrow {fn borrow_mut (& mut self)-> & mut Borrowed;}

But the difference lies in two points:

The standard library implements Borrow and BorrowMut by default for all T, & T and & mut T, taking Borrow as an example:

Impl Borrow for T {fn borrow (& self)-> & T {self}} impl Borrow for & T {fn borrow (& self)-> & T {& * * self}} impl Borrow for & mut T {fn borrow (& self)-> & T {& * * self}}

Borrow requires that the returned type must have the same hash value as the original type. This is a convention, and if this convention is violated, there may be problems putting this type in the HashMap.

From / Into

Both AsRef and Borrow are conversions from & T to & U, while From/Into is from T to U:

Pub trait From: Sized {fn from (_: t)-> Self;} pub trait Into: Sized {fn into (self)-> T;}

Because From and Into are an inverse set of transformations, the standard library provides an implementation:

Impl Into for Twhere U: From, {fn into (self)-> U {U::from (self)}}

The meaning of this code is: if U: From exists, implement Into for type T. In other words, we just need to implement From for the type, and Into will do it automatically. There is also a set of corresponding TryFrom and TryInto in the standard library to deal with conversion errors that may occur during type conversion, so the return value is the Result type.

ToOwned

ToOwned provides a more general function of Clone. Clone creates a new T-type variable from a & T type variable, while ToOwned creates a new U-type variable from a & T type variable. The standard library also provides a default implementation for ToOwned to call the clone method:

Pub trait ToOwned {type Owned: Borrow; fn to_owned (& self)-> Self::Owned; fn clone_into (& self, target: & mut Self::Owned) {* target = self.to_owned ();}} impl ToOwned for Twhere T: Clone, {type Owned = T Fn to_owned (& self)-> T {self.clone ()} fn clone_into (& self, target: & mut T) {target.clone_from (self);}} ToString / FromStr

ToString provides the ability to convert other types to String types:

Pub trait ToString {fn to_string (& self)-> String;}

ToString is implemented by default for all types that implement Displaytrait in the standard library, while Display can be implemented through derive:

Impl ToString for T {default fn to_string (& self)-> String {use fmt::Write; let mut buf = String::new (); buf.write_fmt (format_args! ("{}", self)) .destroy ("a Display implementation returned an error unexpectedly"); buf}}

FromStr provides the ability to convert from string slices to other types:

Pub trait FromStr: Sized {type Err; fn from_str (s: & str)-> Result;} IO

The IO here refers to standard input and output and file input and output.

Standard input and output

The println we introduced earlier! Macros can easily output some information, but if you want finer control over standard input and output, you need to call the std::io::stdin () function and the std::io::stdout () function to get instances of Stdin and Stdout structures. The simple uses of these two examples are as follows:

Use std::io:: {self, Read}; fn main ()-> io::Result {let mut buffer = String::new (); let mut stdin = io::stdin (); stdin.read_to_string (& mut buffer)?; Ok (())}

For thread safety reasons, each read operation needs to be locked, which reduces efficiency. The solution is to call the lock () method manually, but this adds complexity to using standard input and output.

In fact, we are influenced by doing all kinds of large assignments in C language when we are students, which leads us to think that standard input and output is a very important function, but let's think about it, who in a serious command line program will use standard input to interact with users? It is generally done in two ways, one is to specify parameters when the program is called, and the other is to read the user configuration through a file.

File input and output

File input and output must first solve the path problem. The string types in Rust are String and str, both of which are encoded in utf-8. However, utf-8 coding is not used uniformly on specific operating systems. In order to deal with this situation, OsString and OsStr are designed in Rust. These two types are used in a similar way to String and str, and they can also be converted to each other.

The Rust standard library provides std::path::PathBuf and std::path::Path to handle paths, PathBuf has ownership of internal data, while Path just borrows it. In fact, PathBuf stores an OsString internally, while Path stores Path.

The file operation of Rust is mainly completed through std::fs::File, which can realize file operations such as opening, creating, copying and so on. For reading and writing files, you will use some trait in the std::io module, such as Read and Write. File implements these two trait, so it has methods such as read to read files.

Let's look at an example to demonstrate the method of file input and output:

Use std::fs::File;use std::io:: {BufRead, BufReader, Read}; fn test_read_file ()-> Result {let mut path = std::env::current_dir (). Unwrap (); path.push ("Cargo.toml"); let mut file = File::open (& path)?; let mut buffer = String::new (); file.read_to_string (& mut buffer)?; println! ("{}", buffer) Ok (())} fn main () {match test_read_file () {Ok (_) = > {} Err (e) = > {println! ("{}", e);} container

Similar to C++ 's STL, Rust's standard library also provides some commonly used containers and related iterators. The containers that have been implemented are:

Container describes Vec variable length array, continuously stores VecDeque bidirectional queue, suitable for inserting and deleting data LinkedList bidirectional linked list from header and tail, discontiguous storage HashMap stores a series of key pairs based on Hash algorithm, BTreeMap stores a series of key pairs based on B-tree, a series of key pairs HashSet is equivalent to no value HashMapBTreeSet is equivalent to a priority queue implemented by BTreeMapBinaryHeap based on binary heap

I will not go into detail here, but I will delve into the usage and implementation principles of each container in the future.

Iterator

The iterator in Rust refers to the type that implements the trait of std::iter::Iterator, which is defined as follows:

Pub trait Iterator {type Item; fn next (& mut self)-> Option;...}

Its main method is next (), which returns an Option and None when the iteration is complete. It is implemented that the type of Iterator can be used directly in the for loop.

An important feature of an iterator is that it is composable, which is somewhat similar to streaming programming in Java. There are many methods in Iterator, and the types they return also implement Iterator, which means that we can call these methods to create a new iterator from an iterator, for example:

Fn main () {let v = vec! [1, 2, 3, 4, 5, 6, 7, 8]; let mut iter = v. ITER () .take (5) .filter (| & x | x% 2 = = 0) .map (| & x | x * x) .construcerate () While let Some ((I, v)) = iter.next () {println! ("{}: {}", I, v);}}

The meaning of this code is: filter the element from the first five elements of the Vec, require it to be a multiple of 2, and square the element, so the final output is 0: 4 and 1: 16.

Operator overloading

Rust supports custom type overloading partial operators, as long as the type implements the corresponding trait under the std::ops module. Take Add as an example:

Pub trait Add {type Output; fn add (self, rhs: Rhs)-> Self::Output;}

It has a generic parameter RHS and an associated type Output. This trait is implemented for the already basic numeric types in the standard library:

Macro_rules! Add_impl {($t:ty) *) = > ($(# [stable (feature = "rust1", since = "1.0.0")] impl Add for $t {type Output = $t # [inline] # [rustc_inherit_overflow_checks] fn add (self, other: $t)-> $t {self + other}} forward_ref_binop! {impl Add, add for $t, $t})} add_impl! {usize u8 u16 u32 u128 isize i8 i16 i32 i64 i128 f32 f64} here I believe that everyone on the "Rust commonly used standard library workers have what" have a deeper understanding, might as well to the actual operation of it! Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report