I often use rust little crate

Time:2021-12-4

Original link:Karol Kuczmarski’s Blog – Small Rust crates I (almost) always use


Due to the relatively poor standard library of rust, the use of rust will inevitably introduce many third generation dependencies.

These third-party dependencies are used to solve some problems that can be solved by relying on built-in libraries.
A good example is pythonreModule, which is equivalent to rustregex crate。

Problems like regular expressions are a relatively large class of problems, and it’s not surprising to have a dedicated library. For a language, it is less common to provide a small library to solve a very specialized problem.

It’s like a function / type / macro or just a little bigger than them.

In this blog, we will quickly browse a series of necessary “small libraries”

either

Rust has built-inResultType, this isOkandErrA collection of. It forms the basis of general error handling in rust.

Structurally,Result<T, E>Just providedTandEAlternative. You might want to do this
An enumeration class is used for different purposes to represent error handling. Unfortunately, becauseResultStrong internal care
Meaning, this usage is not in line with the rust style, but also puzzling (actuallyResultFrom name to usage
Are highly explicit semantics, which will cause confusion if used in other places)

This is also a needeithercrate。 It contains the followingEitherType:

enum Either<L, R> {
    Left(L),
    Right(L),
}

Although it is related toResultIsomorphism, but it does not have mandatory error handling semantics. And it also provides symmetry
Combiner method, such asmap_leftandright_and_thenFor chain calculationEigtherIncluded values

lazy_static

Because of language design, rust does not allow safe use of global variables. Introduce global variables into your code
The semi standard method is to uselazy_static crate

However, the most important use of this crite is to declare complex constants for delayed initialization:

lazy_static! {
    static ref TICK_INTERVAL: Duration = Duration::from_secs(7 * 24 * 60 * 60);
}

This technique is not completely transparent, but until rust has itRuntime expression, that’s the best way you can think of

maplit

In order to better cooperate with the above crite, and use thevec![]Similar syntax, we can usemaplit

It defines some very simplehashmap!andhashset!Macro, so you can pass “literal
add toHashMapandHashSet

lazy_static! {
    static ref IMAGE_EXTENSIONS: HashMap<&'static str, ImageFormat> = hashmap!{
        "gif" => ImageFormat::GIF,
        "jpeg" => ImageFormat::JPEG,
        "jpg" => ImageFormat::JPG,
        "png" => ImageFormat::PNG,
    };
}

stayhashmap!Inside the macro,hashmap!Will be called according to the incoming literalHashMap::insert, and then returns the value that already contains the incoming literalHashMap

try_opt

Introduced in rust?Before operator(It is now available), processingResultThe usual way to spread errors is to usetry!Macro.

try_optImplement a similar macro for option type for propagationNone, the use of this macro is also quite intuitive:

fn parse_ipv4(s: &str) -> Option<(u8, u8, u8, u8)> {
    lazy_static! {
        static ref RE: Regex = Regex::new(
            r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$"
        ).unwrap();
    }
    let caps = try_opt!(RE.captures(s));
    let a = try_opt!(caps.get(1)).as_str();
    let b = try_opt!(caps.get(2)).as_str();
    let c = try_opt!(caps.get(3)).as_str();
    let d = try_opt!(caps.get(4)).as_str();
    Some((
        try_opt!(a.parse().ok()),
        try_opt!(b.parse().ok()),
        try_opt!(c.parse().ok()),
        try_opt!(d.parse().ok()),
    ))
}

Until rust supports?try_opt!It is an acceptable workaround.

exitcode

Basically, a common convention in every mainstream operating system is that if a process exits with code different from 0 (zero),
Indicates that the process has an error. Linux further divides the space of error code, and together with BSD, it also includes sysexits. H header file, which contains some more professional code.

Many programs and languages use these methods. In rust, you can also use semi standard names for common errors. All you need to do is add exitcode crit to your project dependency:

fn main() {
    let options = args::parse().unwrap_or_else(|e| {
        print_args_error(e).unwrap();
        std::process::exit(exitcode::USAGE);
    });

exceptUSAGEorTEMPFAILIn addition to constants like,exitcode
It also defines an integer type for saving exit codesExitCodeAlias. It can also be used as the return type of a top-level function, among other things:

    let code = do_stuff(options);
    std::process::exit(code);
}

fn do_stuff(options: Options) -> exitcode::ExitCode {
    // ...
}

enum-set

In Java, there is a commonSetSpecialized interface for enumerating types:EnumSet。 Its members are represented very compactly as bits rather than hash elements
Su.

enum_setA similar (though not as strong) structure is implemented. For a#[repr(u32)]Enumeration type:

#[repr(u32)]
#[derive(Clone, Copy, Debug Eq, Hash, PartialEq)]
enum Weekday {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday,
}

You can create a for its membersEnumSet :

let mut weekend: EnumSet<Weekday> = EnumSet::new();
weekend.insert(Weekday::Saturday);
weekend.insert(Weekday::Sunday);

As long as you implement a simple trait that declares how to convert the enumeration value to or from U32:

impl enum_set::CLike for Weekday {
    fn to_u32(&self) -> u32            { *self as u32 }
    unsafe fn from_u32(v: u32) -> Self { std::mem::transmute(v) }
}

The advantage of this is that it has a set structure represented by a single unsigned 32-bit integer. The complexity of all set operations is O (1). These operations include membership check, two sets of unions, their intersection, difference and so on.

antidote

As part of the promise of “fearless concurrency”, ruststd::syncThe module defines many synchronization primitives.
MutexRwLockOne thing in common with their similar mechanisms is that if a thread panics while holding them,
Their locks will become “poisoned”. Therefore, acquiring locks requires handling potential problemsPoisonError

However, for many programs, lock poisoning is not even remote, but a direct and impossible situation.
If you follow the best practice of concurrent resource sharing, you will not hold the lock of multiple instructions, and there is no chance of unpacking or any other panic! ()。
Unfortunately, you can’t prove this statically to the rust compiler, so it still requires you to deal with an impossible poisonerror.

As shown in the nameantidoteThis is where it can help.
In antidote, you can find all the same locking and protection APIs provided by STD:: sync,
Just no poisonerror. In many cases, this deletion fundamentally simplifies the interface,
For example, convert the return type of result < guard, error > to guard.

The cost is obvious. You need to ensure that all threads holding “immune” locks:

  1. No panic at all; perhaps
  2. If they panic, they will not put the protected resources in an inconsistent state

As mentioned earlier, the best way to achieve this goal is to keep the key parts of locking protection in a minimum and absolutely reliable state.

matches

Pattern matching is one of the most important features of rust, but some related language structures have embarrassing shortcomings. For example, if let conditions cannot be used with Boolean tests:

if let Foo(_) = x && y.is_good() {

Therefore, additional nesting or completely different methods are required.

Fortunately, in order to help solve this situation, there is a convenientmatches crate 。
Except for the one with the same namematches!Macro:

if matches!(x, Foo(_)) && y.is_good() {

It also exposes assertion macrosassert_match!anddebug_assert_match!, these macros can be used to produce and test code.

This work adoptsCC agreement, reprint must indicate the author and the link to this article

How many things are never urgent. Heaven and earth turn, time is pressing.

Recommended Today

Hive built-in function summary

1. Related help operation functions View built-in functions: Show functions; Display function details: desc function ABS; Display function extension information: desc function extended concat; 2. Learn the ultimate mental method of built-in function Step 1: carefully read all the functions of the show functions command to establish an overall understanding and impression Step 2: use […]