Original text: github.com/dtolnay/rus…

This document exists to answer frequently asked questions about the Rust programming language. It is not a complete language guide, nor is it a tool for teaching the language. It’s just a reference to answer questions that people in the Rust community often encounter, and to clarify the reasons behind some of Rust’s design decisions.

If you feel there are some common or important questions that are not answered here, please make an issue on GitHub about this repo!

Most of this content was previously in rust-lang/rust and has a dedicated FAQ page on the site. But in the 2018 website redesign, it was removed. I’ve restored it here, because many of these questions are still frequently asked.

directory

  • The Rust Project
  • performance
  • grammar
  • digital
  • string
  • A collection of
  • The ownership of
  • The life cycle
  • The generic
  • Input/output
  • Error handling
  • concurrent
  • The macro
  • Debugging and Tooling
  • Low-Level
  • cross-platform
  • The mod and crate
  • library
  • Design patterns
  • Other languages
  • Documentation

The Rust Project

What is the goal of this project?

Design and implement a secure, concurrent, practical system-level language.

Rust exists because other languages at this level of abstraction and efficiency are unsatisfactory. In particular:

  1. Too little attention is paid to security.
  2. Their support for concurrency is poor.
  3. Lack of practical endurance.
  4. They have limited control over resources.

Rust exists as an alternative that offers both efficient code and a comfortable level of abstraction, while improving on all four of these points.

Is this project controlled by Mozilla?

Rust started in 2006 as a part-time project for Graydon Hoare and remained so for more than three years. Mozilla got involved in 2009 when the language was mature enough to run basic tests and demonstrate its core concepts. While it is still sponsored by Mozilla, Rust was developed by a diverse community of enthusiasts from different parts of the world. The Rust team is made up of Mozilla and non-Mozilla members, and Rust on GitHub has over 2,300 unique contributors to date.

In terms of project management, Rust is managed by a core team that sets the vision and priorities for the project. Guide it from a global perspective. There are also sub-teams to guide and facilitate the development of specific areas of interest, including the core language, compilers, Rust libraries, Rust tools, and the management of the Rust official community. These areas are designed through [RFC] (github.com/rust-lang/r… RFC changes are determined by THE PR of the RUSTC warehouse.

What are some of Rust’s non-goals?

  1. We don’t use any particularly cutting edge technology. Older, mature technologies are better.
  2. We don’t prioritize expressiveness, minimalism, or elegance over other goals. These are desirable, but subordinate goals.
  3. We do not intend to cover the full feature set of C++ or any other language. Rust should provide functionality in most cases.
  4. We don’t intend to be 100% static, 100% secure, 100% reflective, or overly dogmatic in any other sense. There are tradeoffs.
  5. We do not require Rust to run on “every possible platform.” It must ultimately run without unnecessary compromises on widely used hardware and software platforms.

In what projects does Mozilla use Rust?

The main project is Servo, an experimental browser engine that Mozilla is working on. They are also working to integrate Rust components into Firefox.

What are some examples of large Rust projects?

The two biggest Rust open source projects today are Servo and the Rust compiler itself.

Who else uses Rust?

More and more organizations!

How can I easily try Rust?

The easiest way to try Rust is through Playpen, an online application for writing and running Rust code. If you want to try Rust on your system, install it and go through the quiz game tutorial in the book.

How can I get help with Rust problems?

There are several ways. You can be.

  • Post on users.rust-lang.org, Rust’s official user forum
  • Ask questions on the official Rust IRC Channel (# Rust on irc.mozilla.org).
  • Use the “rust” tag to ask questions on Stack Overflow.
  • Post on /r/rust, the unofficial Rust sub-forum

Why has Rust changed so much over time?

The original goal of Rust was to create a secure but usable system programming language. In pursuit of this goal, it explored many ideas, some of which were retained (lifecycle, traits) while others were discarded (type-state systems, green threads). In addition, many standard libraries were rewritten prior to 1.0, as earlier designs were updated to best use Rust’s features and provide high-quality, consistent cross-platform apis. Rust has now reached 1.0, and the language is guaranteed to be “stable”; While it may continue to evolve, code that runs on Rust today should continue to run on future releases.

How does Rust versioning work?

Rust’s language versioning follows SemVer’s lead, allowing backward incompatible changes to stable apis in small versions only when compiler bug fixes, security hole fixes, or more comments are needed to change type inference and distribution. More detailed guidance on minor revisions can be found in the LANGUAGE and library RFC.

Rust has three “release channels” : stable, beta and Nightly. The stable and beta editions are updated every six weeks. The current nightly edition becomes the new beta edition, and the current nightly edition becomes the new stable edition. Language and library features marked as unstable or hidden behind feature switches are available only at nightly. New features come in an unstable form and are “unbanned” once approved by the core team and relevant sub-teams. This approach allows experimentation while providing strong backward compatibility guarantees for stable channels.

For more details, read Rust’s blog post “Stability as a Deliverable.”

Can I use unstable features on beta or stable channels?

No, you can’t. Rust strives to provide strong assurance of the stability of features offered on beta and stable channels. When something is unstable, it means we can’t provide those guarantees for it yet and don’t want people to rely on it to stay the same. This gives us an opportunity to experiment with change at nightly, while still maintaining a strong guarantee for those seeking stability.

Things are stable all the time, with beta and stable channels updated every six weeks and occasional beta fixes at other times. If you are waiting for a feature and don’t use nightly, you can locate its tracker problem by checking the B-Unstable TAB on the problem tracker.

What is a “characteristic switch”?

A “feature switch” is the mechanism Rust uses to stabilize features of compilers, languages, and standard libraries. A feature that is “switched” is accessible only at nightly, and only if explicitly enabled by the #[feature] property or the -z unstable-options command line argument. A feature is considered stable when it is stabilized so that it can be used on a stable distribution channel and does not need to be explicitly enabled. Feature switches allow developers to test experimental features in development before they become available in stable languages.

Why the MIT/ASL2 dual license?

The Apache license includes important protection against patent infringement, but it is incompatible with GPL Version 2. To avoid problems with Rust and GPL2, Rust uses an MIT license.

Why a BSD-style license and not an MPL or triad license?

This is partly due to the preferences of the original developer (Graydon) and partly due to the fact that languages tend to have a wider audience and a greater variety of possible embeddings and end uses than, say, a web browser. We want to attract as many of these potential contributors as possible.

performance

How fast is Rust?

Very fast! Rust already competes with C and C++ in many benchmarks (benchmark games and others, for example).

Like C++, Rust makes zero-cost abstraction one of its core tenets: none of Rust’s abstractions impose a global performance penalty, nor do they impose any run-time system overhead in the traditional sense.

Given that Rust is built on TOP of LLVM and strives to be similar to Clang from the LLVM perspective, any performance improvements to LLVM also help Rust. In the long run, the richer information in Rust’s type system should also enable optimizations that are difficult or impossible to achieve in C/C++ code.

Does Rust have a garbage collection?

No, one of Rust’s key innovations is memory security (no Segfaults) while eliminating the need for garbage collection.

By avoiding GC, Rust can provide many benefits: predictable resource cleanup, low memory management overhead, and essentially no runtime system. All of these features make Rust lean and easy to embed in arbitrary contexts, and make it easier to integrate Rust code with languages that have GC.

Rust avoids the need for GC through its ownership and borrowing system, but the same system also helps solve a host of other issues, including general resource management and concurrency.

When single ownership is not enough, Rust programs rely on the standard reference-counting smart pointer type Rc, and its thread-safe counterpart Arc, rather than GC.

However, we are looking at optional garbage collection as a future extension. Our goal is to make it work smoothly with garbage collection runtimes, such as those provided by Spidermonkey and V8’s JavaScript engines. Finally, some people have looked into implementing a pure Rust garbage collector without compiler support.

Why is my program slow?

Rust compilers do not compile with optimizations unless they are required to do so, because optimizations slow down compilation and are generally undesirable during development.

If you compile with cargo, use the –release flag. If you compile directly with RUSTC, use the -o flag. Either of these flags will turn on optimization.

Rust seems to compile slowly. Why is that?

Code translation and optimization. Rust provides a high level of abstraction that can be compiled into efficient machine code, and these translations take time to run, especially when optimized.

But Rust’s compile time is not as bad as it looks, and there is reason to believe it will improve. When comparing projects of similar size between C++ and Rust, it is generally assumed that the compile times for the entire project are comparable. Rust is generally considered slow to compile, due in large part to the difference between C++ and Rust’s compilation model. Whereas C++’s compilation unit is a file, Rust’s compilation unit is crate made up of many files. Thus, modifying a C++ file during development can result in much less recompilation than Rust. Efforts are under way to refactor the compiler to introduce incremental compilation, which will provide Rust with the compile time advantage of the C++ model.

In addition to the compilation model, there are several other aspects of Rust’s language design and compiler implementation that affect compile-time performance.

First, Rust has a moderately complex type system, and a nonnegligible compile time must be spent enforcing constraints to make Rust safe at run time.

Second, the Rust compiler has long-term technical debt, notably producing LLVM IR of poor quality, which LLVM must spend time “fixing.” Adding a new internal representation, called MIR, to the Rust compiler makes it possible to do more optimizations to improve the quality of the LLVM IR generated, but this has not yet happened.

Third, Rust’s use of LLVM to generate code is a double-edged sword: while it gives Rust world-class runtime performance, LLVM is a large framework that doesn’t focus on compile-time performance, especially when dealing with poor-quality input.

Finally, while Rust’s preferred strategy is singleton generics (similar to C++), it requires much more code to be generated than other translation strategies. Rust programmers can use feature objects in exchange for this bloat of code by using dynamic scheduling.

Why did Rust’sHashMapVery slow?

By default, Rust’s HashMap uses the SipHash hash algorithm, which is designed to prevent hash collision attacks while providing reasonable performance under a variety of workloads.

While SipHash shows competitive performance in many cases, the one case where it is significantly slower than other hashing algorithms is in short keys, such as integers. This is why Rust programmers often observe slow performance on HashMap. In such cases, FNV Hasher is often recommended, with the caveat that it does not have the same crash-resistant properties as SipHash.

Why is there no integrated benchmark infrastructure?

Yes, but it’s only available on the nightly. We eventually plan to build a pluggable system for comprehensive benchmarking, but in the meantime, the current system is considered unstable.

Does Rust do tail-call optimization?

Generally speaking, no. Tail-call optimization may occur in limited cases, but is not guaranteed. Since this feature has always been desired, Rust retains a keyword (become), although it is not yet clear whether it is technically feasible or whether it will ever be implemented. There was a proposed extension to allow the elimination of trailing calls in some cases, but it has been delayed for now.

Does Rust have runtime?

Not run-time in the typical sense used by languages like Java, but parts of the Rust standard library can be considered “run-time,” providing a heap, traceback, unpack, and stack daemon. There is a small amount of initialization code that runs before the user’s main function. The Rust library also links to the C library, which does similar runtime initialization. Rust code can be compiled without a standard library, in which case it is roughly run-time equivalent to C.

grammar

Why curly braces? Why can’t Rust have the same syntax as Haskell or Python?

Using curly braces to represent blocks is a common design choice in various programming languages, and Rust’s consistency is useful for those already familiar with the style.

Braces can also provide programmers with more flexible syntax and a simpler parser in the compiler.

I can be inifThere are no curly braces on the condition, so why do I have to put curly braces around a single line block? Why not use the C language style?

The C language requires bracketed conditions for “if” statements, but braces are optional, and Rust makes the opposite choice for its “if” expressions. This keeps the conditional statement clearly separate from the statement body and avoids the hazards of optional braces, which can lead to easily overlooked errors during refactoring, such as Apple’s Goto Fail error.

Why is there no literal grammar for dictionaries?

Rust’s overall design tends to limit the size of the language while enabling powerful libraries. While Rust does provide an initialization syntax for array and string literals, these are the only collection types in the language. Other library-defined types, including the ubiquitous Vec collection types, are initialized using macros, such as Vec! Macro.

This design choice of using Rust’s macro facilities to initialize collections may be extended to other collections in general in the future, to initialize not only HashMap and Vec simply, but also other collection types, such as BTreeMap. In the meantime, if you want a more convenient syntax for initializing collections, you can create your own macros to provide it.

When should I use implicit returns?

Rust is a very expression oriented language, and “implicit return” is part of that design. Structures like IFs, matches, and normal blocks are expressions in Rust. For example, the following code checks if an i64 is odd and returns the result simply by taking it as a value.

fn is_odd(x: i64) - >bool {
    if x % 2! =0 { true } else { false}}Copy the code

Although it can be further simplified, for example.

fn is_odd(x: i64) - >bool {
    x % 2! =0
}
Copy the code

In each case, the last line of the function is the return value of the function. Note that if a function ends with a semicolon, its return type will be (), indicating that no value is returned. An implicit return must omit the semicolon in order to work.

An explicit return is used only when an implicit return is not possible, because you want to return before the end of the function body. Although each of the above functions could be written with a return keyword and semicolon, doing so is unnecessarily verbose and inconsistent with the conventions of Rust code.

Why not infer the signature of the function?

In Rust, declarations tend to have explicit types, whereas the type of the actual code is inferred. There are several reasons for this design:

  • Mandatory declaration signatures help enforce interface stability at the module and plate level.
  • Signatures improve the programmer’s understanding of the code, eliminating the need for the IDE to run inference algorithms across the board to guess the type of arguments to a function; It’s always explicit, it’s nearby.
  • Mechanically, it simplifies reasoning algorithms because reasoning requires only looking at one function at a time.

whymatchIt has to be exhaustive, right?

To aid in refactoring and clarity.

First, if every possibility is covered by match, adding variants to the enum in the future will result in compile failures rather than run-time errors. This type of compiler helps make fearless refactoring possible in Rust.

Second, brute checking makes the semantics of the default clear: in general, the only safe way to make a non-brute match is to panic a thread if nothing is matched. Earlier versions of Rust did not require the match case to be exhaustive, and found it to be a big source of bugs.

By using the _ wildcard, it is easy to ignore all unspecified cases.

match val.do_something() {
    Cat(a) => { / *... * /} _ = > {/ *... * /}}Copy the code

Numerics

For floating-point arithmetic, I should choosef32andf64Which one of them?

The choice depends on the purpose of the program.

If you are interested in maximum accuracy for floating-point numbers, choose F64. If you are more interested in maintaining the size or maximum efficiency of values, and don’t care about the error associated with having fewer bits per value, then F32 is better. Operation on f32 is generally faster, even on 64-bit hardware. As a common example, graphics programming often uses F32 because it requires high performance, and 32-bit floating-point numbers are sufficient to represent pixels on the screen.

If in doubt, choose the F64 for greater accuracy.

Why can’t I compare floating point numbers or use them asHashMaporBTreeMapThe key?

Floating point numbers can be used with ==,! The =, <, <=, >, and >= operators, as well as the partial_cmp() function. = = and! = is part of the PartialEq feature, while <, <=, >, >= and partial_cMP () are part of the PartialOrd feature.

Floating-point numbers cannot be compared using the CMP () function, which is part of the Ord feature because floating-point numbers have no total sort. Furthermore, floating point numbers do not have an congruent relationship, so they also do not implement Eq.

Since a floating point NaN is not less than, greater than, or equal to any other floating point number or itself, floating point numbers have no total ordering or equality relationship.

Because floating-point numbers do not implement Eq or Ord, they cannot be used for types whose attribute boundaries require these attributes, such as BTreeMap or [HashMap]. This is important because these types assume that their keys provide a total ordering or total equivalence relationship that would otherwise fail.

One Crate packs F32 and F64 to provide implementations of Ord and Eq, which can be useful in some cases.

How do I convert between numeric types?

There are two methods: the AS keyword, which does simple conversions to primitive types, and the Into and From features, which are implemented for some type conversions (you can also implement them for your own types). The Into and From features are implemented only if the transformation is lossless, so for example, f64:: From (0f32) will compile, but f32:: From (0f64) will not. As, on the other hand, will convert between any two primitive types, truncating values if necessary.

Why does Rust have no increment and decrement operators?

Preincrement and Postincrement (and their opposite, Decrement) are convenient but complicated. They require an understanding of the order of computation and often result in subtle errors and undefined behavior in C and C++. X plus 1 is just a little bit longer than x equals x plus 1, but it’s not clear.

string

How to combine aStringorVec<T>Convert to a fragment (&strand&[T])?

Typically, you can pass a reference to String or Vec

where you expect a fragment. Using Deref Coercions, Strings, and Vecs will automatically concatenate to their respective pieces when passing references with & or &mut.

Methods implemented on &str and &[T] have direct access to String and Vec

. For example, some_string.trim() works, even though trim is a method on & STR and some_string is a String.

In some cases, such as generic code, manual conversion is necessary. Manual conversion can be done using the slice operator, like this. &my_vec[…] .

How do I from&strThe switch toStringOr vice versa?

The to_string() method converts &str to String. When you borrow a reference, String is automatically converted to &str. Both are illustrated in the examples below.

fn main() {
    let s = "Jane Doe".to_string();
    say_hello(&s);
}

fn say_hello(name: &str) {
    println! ("Hello {}!", name);
}
Copy the code

What is the difference between two different string types?

String is a private buffer of UTF-8 bytes allocated on the heap. Mutable strings can be modified to increase their capacity as needed. &str is a fixed-capacity “view” of a String allocated elsewhere, usually on the heap if a fragment is referenced from a String, or in static memory if a String literal.

& STR is a primitive type implemented by the Rust language, while String is implemented by the standard library.

How do I get in oneStringFor O(1) character access?

You can’t. At least not if you don’t know what “character” means, and if you’re preprocessing the string to find the index of the desired character.

The Rust string is UTF-8 encoded. A single visual character in UTF-8 is not necessarily a byte, because it is a byte in an ASCII encoded string. Each byte is called a “code unit” (in UTF-16, a code unit is two bytes; In UTF-32 it is 4 bytes). A “code point” consists of one or more code units that are combined into a “grapheme group” that most closely resembles a character.

So even if you can index bytes in a UTF-8 string, you can’t access the i-th code point or letter group in constant time. However, if you know which byte the desired code point or glyph group starts from, then you can access it in constant time. Functions including STR ::find() and regex matching return a byte index to facilitate this access.

Why is the string default UTF-8?

The STR type is UTF-8, because we’ve seen more text encoded this way in the wild — especially in network transport, where it’s Endian-Agnostic — and we thought it was best not to let the default processing of I/O involve re-encoding code points in each direction.

This does mean that locating a particular Unicode encoding point in a string is an O(n) operation, although if the starting byte index is already known, then they can be accessed in O(1). On the one hand, this is clearly undesirable; On the other hand, this question is fraught with trade-offs, and we want to point out a few important qualifiers.

Scanning code points in the ASCII range of a STR can still be done safely byte by byte. If you use.as_bytes(), fetching a U8 costs O(1) and produces a value that can be converted and compared to a CHAR in the ASCII range. Therefore, if you (for example) break lines on \n, the byte-based processing is still valid. This is how UTF-8 was carefully designed.

Most “character-oriented” text operations work only under very limited language assumptions, such as “code points in the ASCII range only”. Outside the ASCII range, you often have to use complex (non-constant time) algorithms to determine the boundaries of language units (glyphs, words, paragraphs). We recommend an “honest” language-aware, Unicode-approved algorithm.

The char type is UTF-32. If you are sure you need to do a code point algorithm, it is very easy to write a type WSTR = [char] and unpack a STR into it all at once, then work with WSTR. In other words: If you need to use this encoding, the fact that the language does not decode to UTF32 by default should not prevent you from decoding (or recoding in any other way).

For information on why UTF-8 is often more popular than UTF-16 or UTF-32, read the UTF-8 Everywhere Manifesto.

What string type should I use?

Rust has four pairs of string types, each with a different purpose. In each pair, there is a “own” string type and a “shard” string type. It looks something like this.

“Slice” type “Owned” type
UTF-8 str String
OS-compatible OsStr OsString
C-compatible CStr CString
System path Path PathBuf

The different string types of Rust serve different purposes. String and STR are utF-8 encoded generic strings. OsString and OsStr are encoded according to the current platform and are used when interacting with the operating system. CString and CStr are equivalent to strings in C and are used in FFI code. PathBuf and Path are convenient wrappers for OsString and OsStr, providing path-specific methods for operation.

How can I write an acceptance&strAnd acceptStringThe function?

There are several options, depending on the needs of the function.

  • If a function needs a string of its own, but wants to accept strings of any type, it can use oneInto<String>Binding.
  • If the function needs a string shard but wants to accept any type of string, useAsRef<str>Binding.
  • If the function does not care about the type of the string and wants to handle both possibilities equally, useCow<str>As an input type.

useInto<String>

In this example, the function will take both its own string and its string slice, either doing nothing or converting the input string to its own string within the function body. Note that the transformation needs to happen explicitly or it won’t happen.

fn accepts_both<S: Into<String>>(s: S) {
    let s = s.into(); // This will convert s to a String.
    / /... The rest of the functions
}
Copy the code

useAsRef<str>

In this case, the function will take an owned string and a string fragment and either do nothing or convert the input string fragment to a string. This can be done automatically by reference input, like this.

fn accepts_both<S: AsRef<str>>(s: &S) {
    / /... The body of the function
}
Copy the code

useCow<str>

In this example, the function receives a Cow< STR >, which is not a generic type, but a container that contains its own string or string fragment as needed.

fn accepts_cow(s: Cow<str>) {
    / /... The body of the function
}
Copy the code

A collection of

Can I effectively implement data structures such as vectors and linked lists in Rust?

If your reason for implementing these data structures is to use them in other programs, there is no need, since effective implementations of these data structures are already provided by the standard library.

However, if your reason is just to learn, you probably need to dabble in unsafe code. While these data structures can be implemented entirely with Rust, which is secure, performance may be worse than using less secure code. The reason is simple: data structures such as vectors and linked lists rely on Pointers and memory operations that are not allowed in security Rust.

For example, a double-linked list requires two mutable references per node, which violates Rust’s mutable reference alias rules. You can solve this problem with Weak

, but the performance will be worse than you want. With insecure code, you can get around the variable-reference alias rule, but you must manually verify that your code introduces a memory safety violation.

How can I iterate over the collection without moving/consuming it?

The simplest way to do this is through an IntoIterator implementation using collections. Here is an example of &VEC.

let v = vec! [1.2.3.4.5];
for item in &v {
    print! ("{}", item);
}
println! ("\nLength: {}", v.len());
Copy the code

Rust’s for loops call into_iter() (defined in the IntoIteratortrait) for the thing they are iterating over. Anything that implements the IntoIteratortrait can be looped through with a for loop. IntoIterator is implemented for &vec and &mut Vec, causing iterators from into_iter() to borrow the contents of the collection instead of moving/consuming them. This is also true for other standard sets.

If you need a move/consume iterator, write the for loop without using & or &mut in the iteration.

If you need direct access to a borrowed iterator, you can usually get it by calling iter().

Why do I need to enter the array size in the array declaration?

You don’t have to do this. If you declare an array directly, the size is inferred from the number of elements. But if you’re declaring a function that accepts a fixed-size array, the compiler must know how big the array is.

One thing to note is that Rust does not currently provide generics for arrays of different sizes. If you want to accept a continuous container with a variable number of values, use Vec or slice (depending on whether you need ownership).

The ownership of

How can I implement a graph or other data structure that contains rings?

There are at least four options (discussed in detail in Too Many Linked Lists).

  • You can implement this using Rc and Weak to allow shared ownership of nodes. Although there are memory management costs associated with this approach.
  • You can implement it using “unsafe” code, using raw Pointers. This would be more efficient, but circumvent Rust’s security guarantee.
  • Use vectors and indexes of those vectors. There are several examples and explanations of this approach.
  • Use borrowed references with UnsafeCell. There is explanation and code for this method.

How can I define a structure that contains a reference to one of its own fields?

It’s possible, but it won’t work. The structure is permanently borrowed by itself and therefore cannot be moved. Here’s some code to illustrate the problem.

use std::cell::Cell;

#[derive(Debug)]
struct Unmovable<'a> {
    x: u32,
    y: Cell<OptionThe < &'a u32> >. }fn main() {
    let test = Unmovable { x: 42, y: Cell::new(None)}. test.y.set(Some(& test. X)).println! ("{:? }", test);
}
Copy the code

What is the difference between value passing, consuming, moving, and transferring ownership?

These are different terms for the same thing. In all cases, this means that the value has been transferred to another owner and is out of the possession of the original owner, who can no longer use it. If a type implements Copy, the original owner’s value is not deprecated and can still be used.

Why is it that some types of values can be used after being passed to a function, while repeated use of other types of values causes errors?

If a type implements Copy, it is copied when passed to a function. All numeric types in Rust implement Copy, but structural types do not by default, so they are moved. This means that the structure can’t be used anywhere else except to move it back out of the function by returning.

How to handle the error “Use of Moved Value”?

This error means that the value you want to use has been transferred to a new owner. The first thing to check is whether the movement in question is necessary: if it moves to a function, perhaps you can rewrite the function to use a reference instead of a movement. Otherwise, if the type being moved implements Clone, calling Clone () on it before moving will move a copy of it, leaving the original usable. Note, however, that cloning a value should usually be a last resort, as cloning can be expensive and lead to further allocation.

If the moved value is your own custom type, consider implementing Copy (for implicit Copy, not movement) or Clone (explicit Copy). The most common way to implement Copy is #[derive(Copy, Clone)] (Copy requires Clone), and Clone (‘ #[Clone (Clone)].

If this is not possible, you may want to modify the function that acquires ownership so that the ownership of the value is returned when the function exits.

Used in method declarationsself,&selfor&mut selfWhat are the rules?

  • Used when a function needs to consume valuesself.
  • Used when a function requires only a read-only reference to a value&self.
  • Used when a function needs to change the value without consuming it&mut self.

How can I understand the borrowing inspector?

The borrowing inspector only applies a few rules when evaluating Rust code, which can be found in the Borrowing section of the Rust book. The rules are:

First, any borrowing must continue no further than the owner’s scope. Second, you can have one or the other of these two borrowings, but not both:

  • One or more references to a resource (&t).
  • A mutable reference (&mut T).

While the rules themselves are simple, adhering to them consistently is not easy, especially for those who are not used to reasoning about longevity and ownership.

The first step to understanding the borrowing inspector is to read the errors it produces. A great deal of work has been done to ensure that the borrowing inspector provides quality help in solving the problems it finds. When you run into problems borrowing checkers, the first step is to read the reported errors slowly and carefully, and only after you understand the described errors can you approach the code.

The second step is to familiarize yourself with the ownership and variability related container types provided by the Rust library, including Cell, RefCell, and Cow. These are useful and necessary tools for expressing some ownership and variability, and are written with minimal performance costs.

One of the most important parts of understanding the borrowing inspector is the practice. Rust’s strong static analysis guarantees are rigorous and quite different from what many programmers have done before. It takes a while to get used to everything.

If you find yourself struggling with borrowing checkers, or running out of patience, feel free to contact the Rust Community for help.

What timeRcUseful?

This is covered in the official Rc documentation, Rust’s pointer type for non-atomic reference calculations. In short, Rc and its thread-safe cousin Arc are useful for expressing shared ownership, which is automatically cancelled when no one accesses the associated memory.

How do I return a closure from a function?

To return a closure from a function, it must be a “move closure,” that is, the closure is declared with the move keyword. As Rust explains, this allows a closure to have its own copy of the captured variables, independent of its parent stack frame. Otherwise, returning a closure would be unsafe because it would allow access to variables that are no longer valid; In other words: it will allow reading of memory that may be invalid. The closure must also be wrapped in a Box so that it is allocated on the heap. Read more about this in the book.

What is deref Coercion and how does it work?

Deref Coercion is a convenient coercion. Automatically converts a reference to a pointer (for example, &rc

or &box

) to a reference to its contents (for example, &t). The Deref Coercion exists to make the use of Rust more ergonomic and is implemented through the Deref feature.

The Deref implementation shows that the implementation type can be converted to the target type by calling the Deref method, which receives an immutable reference to the invocation type and returns a reference to the target type (with the same lifecycle). The prefix operator is shorthand for the deref method.

They are called “coercions” because of the following rules, which are referenced in Rust.

If you have a type U and it implements Deref

, the value of &u will automatically be forced to be T.
=t>

For example, if you have an &Rc

, it will be federated to an &String by this rule, and then federated to an & STR in the same way. Therefore, if a function needs an &str argument, you can pass an &rc

directly, and all coercion is handled automatically by the Deref feature.

The most common types of Derefcoercions are:

  • &Rc<T>to&T.
  • &Box<T>to&T.
  • &Arc<T>to&T.
  • &Vec<T>Instead of&[T].
  • &StringInstead of&str.

The life cycle

Why life cycle?

The life cycle is Rust’s answer to the memory security question. It allows Rust to ensure memory security without the performance cost of garbage collection. They are based on a variety of academic work.

Why is the lifecycle syntax like this?

The ‘a syntax comes from the ML family of programming languages, where ‘a is used to represent a generic type parameter. For Rust, this syntax must be unambiguous, obvious, and suitable for use with traits and reference in type declarations. Other grammars have been discussed, but no other grammars have been proven to be better.

How do I return a borrowed thing to something I created from a function?

You need to make sure that the borrowed stuff outlives the function. This can be done by binding the output life to some input life, for example.

type Pool = TypedArena<Thing>;

// The following lifecycle is written explicitly for illustrative purposes only; It can be omitted by the deletion rule described later.
fn create_borrowed<'a>(pool: &'a Pool,
                       x: i32,
                       y: i32) - > &'a Thing {
    pool.alloc(Thing { x: x, y: y })
}
Copy the code

Another approach is to eliminate references entirely by returning a self-contained type such as String.

fn happy_birthday(name: &str, age: i64) - >String {
    format! ("Hello {}! You're {} years old!", name, age)
}
Copy the code

This approach is simpler, but often leads to unnecessary allocation.

Why do some references have longevity, such as&'a TWhile others do not, such as&T?

In fact, all reference types have a lifetime, but most of the time you don’t have to explicitly write that it is explicit. The rules are as follows.

  1. In a function body, you never need to write out the life cycle explicitly; The correct value should always be inferred.

  2. In the signature of a function (for example, in the type of its arguments or its return type), you may need to write a life cycle. The lifecycle here uses a simple default scheme called “Lifetime elision”. It consists of the following three rules:

    • In a function’s argument, each omitted lifecycle becomes a separate lifecycle parameter.
    • If there is exactly one input life cycle, whether omitted or not, that life cycle is assigned to the omitted life cycle of all returned values.
    • If there are more than one input life cycle, but one of them is &self or &mut self, then the life cycle of self is assigned to all ignored return life cycles.
  3. Finally, in the definition of “structure” or “enumeration,” all lifecycles must be explicitly declared.

If these rules result in a compilation error, the Rust compiler will provide an error message indicating the error caused and suggest a potential solution based on which step in the reasoning process caused the error.

How does Rust guarantee “no null pointer” and “no dangling pointer”?

The only way to construct a value of type &foo or &mut Foo is to specify a reference to an existing value of type Foo to which it points. References “borrow” original values within a given area of code (the lifetime of the reference), during which borrowed values cannot be moved or destroyed.

How do I use “null” to express a missing value?

You can do this with the Option type, which can be Some(T) or None. Some(T) indicates that it contains a value of type T, and None indicates that there is no value.

The generic

What is “singularization”?

Singularization is the singularization of each use of a generic function (or structure) with a specific instance based on the type of argument that calls the function (or uses the structure).

In singularization, a new copy of a generic function is translated into each set of unique types that the function instantiates. This is the same strategy used by C++. The result is fast code designed specifically for each call point and statically scheduled, at the cost of “code bloat,” where multiple function instances result in larger binaries than those created with other translation strategies.

Functions that take a Trait Object instead of a type parameter are not singularized. Instead, methods on attribute objects are allocated dynamically at run time.

What’s the difference between a function and a closure that doesn’t capture any variables?

Functions and closures are operationally equivalent, but have different runtime representations because of their different implementations.

Functions are the built-in primitives of the language, and closures are essentially syntactic sugar for one of the three characteristics. Fn, FnMut, and FnOnce. When you create a closure, the Rust compiler automatically creates a structure that implements the corresponding features of the three structures, takes the captured environment variables as members, and makes the structure available as a function. Exposed functions cannot capture the environment.

The biggest difference between these features is how they accept the “self” argument. Fn uses &self, FnMut uses &mut self, and FnOnce uses self.

Even if a closure does not capture any environment variables, it is represented at run time as two Pointers, the same as other closures.

What are higher-order types, why do I need them, and why does Rust not have them?

Advanced types are types that have unfilled parameters. Type constructors such as Vec, Result, and HashMap are examples of high-type types: each type requires some additional type parameters in order to actually represent a particular type, such as Vec

. Support for high types means that these “incomplete” types can be used anywhere “full” types can be used, including generics as functions.

Any complete type like i32, bool, or char is of type * (this notation comes from the field of type theory). A type that takes one argument, like Vec

, is belonging to * -> *, meaning that Vec

receives a complete type, like i32, and returns a complete type, Vec

. A type with three parameters, such as HashMap

, is a * -> * -> * -> * and accepts three complete types (such as i32, String, and RandomState), Generate a new complete type HashMap

.
,>
,>


In addition to these examples, the type constructor can also take a lifecycle parameter, which we represent as Lt. For example, the type of slice::Iter is Lt -> * -> *, because it must be instantiated like Iter<‘a, u32>.

The lack of support for higher-order types makes it difficult to write generic code for certain types. This is especially problematic for abstractions of concepts like iterators, which are usually parameterized for at least one lifetime. This, in turn, prevents the creation of abstract traits for Rust collections.

Another common example is a concept like functors or Monads, both of which are type constructors rather than single types.

Rust does not currently support high-type typing because it is not a priority compared to other improvements we would like to make. Since the design is a major, cross-disciplinary change, we also wanted to be careful with it. But there is no intrinsic reason for the current lack of support.

Image in common type<T=Foo>What does this named type parameter mean?

These are called association types, and they allow the expression of characteristic boundaries that cannot be expressed in a WHERE clause. For example, a generic constraint X: Bar

means “X must implement the trait Bar, and in the implementation of Bar, X must select Foo as the association type T for Bar “. Examples where this constraint cannot be expressed through a WHERE clause include trait objects such as Box

>.

=foo>

Associated types exist because generics often involve a family of types, one of which determines all the other types in a family. For example, a graph trait might have the graph itself as its Self type and have associated types of nodes and edges. The type of each diagram uniquely determines the related type. Using associative types makes the work of these type families simpler and in many cases provides better type reasoning.

Can I overload the operator? What operators and how do they operate?

You can use their associative properties to provide custom implementations for various operators. Add stands for +, Mul for *, and so on. It looks something like this.

useSTD: : ops: : Add.struct Foo;

impl Add for Foo {
    type Output = Foo;
    fn add(self, rhs: Foo) -> Self::Output {
        println!("Adding!");
        self}}Copy the code

The following operators can be overloaded.

Operation Trait
+ Add
+ = AddAssign
binary - Sub
- = SubAssign
* Mul
* = MulAssign
/ Div
/ = DivAssign
unary - Neg
% Rem
% = RemAssign
& BitAnd
& = BitAndAssign
| BitOr
| = BitOrAssign
^ BitXor
^ = BitXorAssign
! Not
<< Shl
< < = ShlAssign
>> Shr
> > = ShrAssign
* Deref
mut * DerefMut
[] Index
mut [] IndexMut

Why do you want toEq/PartialEqandOrd/PartialOrdBetween?

In Rust, there are some types of values that are only partially sorted, or only partially equal. Partial sorting means that there can be values in a given type that are neither less than nor greater than each other. Partial equality means that there may be values of a given type that are not equal to themselves.

Floating point types (F32 and F64) are good examples of each. Any floating point type can have a value of NaN (meaning “not a number”). NaN is not equal to itself (NaN == NaN is false), nor less than or greater than any other floating point value. Thus, both F32 and [F64] implement PartialOrd and PartialEq, but not Ord and ‘ ‘Eq’]Eq.

As explained in the previous question about floats, these differences are important because some sets rely on total ordering /equality in order to give the correct result.

Input/output

How do I read a file as a “string”?

Use the read_to_string() method, which is defined on the Read property in STD :: IO.

use std::io::Read;
use std::fs::File;

fn read_file(path: &str) - >Result<String, std::io::Error> {
    let mut s = String::new();
    let_ = File::open(path)? .read_to_string(&mut s);  // `s` contains the contents of "foo.txt"
    Ok(s)
}

fn main() {
    match read_file("foo.txt") {
        Ok(_) = >println!("Got file contents!"),
        Err(err) => println!("Getting file contents failed with error: {}", err)
    };
}
Copy the code

How to read file input efficiently?

The File type implements the Read feature, which has various functions for reading and writing data, including Read (), read_to_end(), bytes(), chars(), and take(). Each of these functions reads a certain amount of input from a given file. Read () reads the amount of input the underlying system can provide in a single call. Read_to_end () reads the entire buffer into a vector, allocating as much space as it needs. Bytes () and chars() allow you to iterate over bytes and characters of a file, respectively. Finally, take() allows you to read any number of bytes from a file. Overall, these should allow you to efficiently read in whatever data you need.

For buffered reads, the BufReader structure is used, which helps reduce the number of system calls at read time.

How do I do asynchronous input/output in Rust?

Use tokio.

How do I get command line arguments in Rust?

The simplest approach is to use Args, which provides an iterator for input parameters.

If you’re looking for a more powerful library, there are some options on Crates. IO.

Error handling

Why is Rust without exceptions?

Exceptions complicate the understanding of control flow, they express validity/invalidity outside of the type system, and they have poor interoperability with multithreaded code (the main focus of Rust).

Rust prefers a type-based error handling approach, which is described in detail in the book. This fits much better with Rust’s control flow, concurrency, and everything else.

What’s with ‘unwrap()’ everywhere?

Unwrap () is a function that extracts the values in Option or Result, and panic if there is no value.

Unwrap () should not be your default way to handle expected errors, such as incorrect user input. In production code, it should be treated as an assertion that the value is non-null, which, if violated, will crash the program.

It’s also useful for rapid prototyping, where you don’t want to deal with errors yet, or in blog posts where error handling distracts from the main point.

When I try to run usingtry!Macro example code when why do I get an error?

This may be due to the return type of the function. try! The macro either extracts the value from Result or returns it early, and Result carries the error. This means that the try is only valid for functions that return Result itself, where Err constructs a type that implements From:: From (Err). In particular, this means try! Macros do not work in main.

Is there an easier way to do error handling than to have “Result” everywhere?

If you’re looking for a way to avoid processing Result in someone else’s code, there’s always unwrap(), but that’s probably not what you want. Result is an indicator that some calculation may or may not complete successfully. Asking you to explicitly address these failures is one of the ways Rust encourages robustness. Rust provides services like Try! Macro tools like this make the process of handling failures ergonomic.

If you really don’t want to handle errors, you can use unwrap(), but be aware that doing so means the code panic when it fails, which usually causes the process to shut down.

concurrent

Can I use static values across threads without “unsafe” blocks?

If it is synchronous, the changes are safe. Modifying a static Mutex (lazily initialized by lazy-static Crate) does not require an unsafe block, as does modifying a static AtomicUsize (which can be initialized without lazy_static).

More generally, if a type implements Sync and does not implement Drop, it can be used in static.

The macro

Can I write a macro to generate an identifier?

Not at the moment. Rust’s macros are “sanitary macros” that deliberately avoid capturing or creating identifiers that might accidentally collide with other identifiers. Their functionality is markedly different from the style of macros typically associated with C preprocessors. Macro calls can only occur where they are explicitly supported: projects, method declarations, statements, expressions, and patterns. In this case, “method declaration” refers to a space where a method can be placed. They cannot be used to complete partial method declarations. By the same logic, they cannot be used to complete a partial variable declaration.

Debugging and Tooling

How do I debug Rust programs?

Rust programs can be debugged using GDB or LLDB, the same as C and C++. In fact, every Rust installation comes with one or both of rust-GDB and rust-LLDB (depending on platform support). These are encapsulation of GDB and LLDB with Rust pretty- Printing enabled.

rustcA panic has occurred in the library code. How do I locate errors in my code?

This error is usually caused by unwrap()ing a None or Err in the client code. You can help get more information by enabling backtracking by setting the environment variable RUST_BACKTRACE=1. Compiling in debug mode (default: “cargo Build”) also helps. Using a debugger, such as the provided rust-gDB or rust-llDB, is also helpful.

What IDE should I use?

There are many options for Rust’s development environment, all of which are detailed on the unofficial IDE support page.

Low-Level

How can ImemcpyByte?

If you want to safely clone an existing shard, you can use clone_from_slice.

To copy bytes that may overlap, use copy. To copy non-overlapping bytes, use copy_nonoverlapping. Both functions are “insecure” because they can both be used to break the language’s security guarantees. Be careful when using them.

Can Rust function reasonably without a standard library?

B: Sure. Rust programs can use #! The [no_std] property is set to not load the standard library. With this property set, you can continue to use the Rust core library, which is just a platform-independent primitive. Therefore, it does not include IO, concurrency, heap allocation, and so on.

Can I write an operating system in Rust?

Yes! In fact, there are several ongoing projects that do just that.

How do I read and write numeric types such as large or decimal in a file or other byte streami32orf64?

You should check out ByteOrder Crate, which provides the corresponding utility.

Does Rust guarantee a specific data layout?

Not by default. In general, the layout of enums and structs is undefined. This allows the compiler to make potential optimizations, such as reusing padding for discriminations, compressing variations of nested enEms, reordering fields to remove padding, and so on. Enums (” c-like “) that do not carry data are eligible to have a defined representation. Such enumerations are easy to distinguish because they are just a list of names without data.

snum CLike {
    A,
    B = 32,
    C = 34,
    D
}
Copy the code

The # [repr(C)] property can be applied to these “enums” so that they have the same representation in the same C code. This allows the “enum” of Rust to be used in FFI code, but also the “enum” of C in most cases. This property can also be applied to structs to obtain the same layout as a C struct.

cross-platform

What are the customary ways to express platform-specific behavior in Rust?

Platform-specific behavior can be expressed with conditional compilation attributes, such as target_OS, target_family, target_endian, and so on.

Can Rust be used for Android/iOS programming?

Yes, it can! There are already examples of using Rust in both Android and iOS. It does take some work to set up, but Rust functions well on both platforms.

Can I run my Rust program in a Web browser?

Possible. Rust has experimental support for both ASM.js and WebAssembly.

How do I cross-compile in Rust?

Cross-compilation is possible in Rust, but it takes a little work to set up. Every Rust compiler is a cross-compiler, but the library needs to be cross-compiled for the target platform.

Rust does distribute copies of the standard library for each supported platform, which are included in the rust-std-* files of each build directory found on the distribution page, but there is currently no way to install them automatically.

The mod and crate

What is the relationship between Mod and Crate?

  • Crate is a compilation unit that is the minimum amount of code that the Rust compiler can operate on.
  • The mod is a (possibly nested) code organization unit in Crate.
  • A Crate contains an implicit, unnamed top-level mod.
  • Recursive definitions can span mods, but not Crate.

Why can’t the Rust compiler find what I’m doinguseThis library of?

There are many possible answers, but a common mistake is not realizing that the use declaration is relative to Crate Root. Try rewriting your declarations, using the paths they define in your project root file, and see if you can fix this.

There are also “self” and “super”, which distinguish the “use” path relative to the current mod or parent mod, respectively.

For complete information about the Use library, read the chapter “Packages, Crates, and Modules” in Rust.

Why do I have to use it from Crate’s top floormodDeclare mod files instead of directlyuseThem?

In Rust, there are two ways to declare modules, inline or in another file. Here is an example of each.

// In main.rs
mod hello {
    pub fn f() {
        println!("hello!"); }}fn main() {
    hello::f();
}
Copy the code
// In main.rs
mod hello;

fn main() {
    hello::f();
}

// In hello.rs
pub fn f() {
    println!("hello!");
}
Copy the code

In the first example, the module is defined in the same file it uses. In the second example, the module declaration in the main file tells the compiler to look for Hello. rs or hello/mod.rs and load the file.

Note the difference between mod and use: mod declares the existence of a module, while use references a module declared elsewhere, bringing its contents into the scope of the current module.

How do I configure Cargo to use a proxy?

Reference rsproxy. Cn /.

Why can’t the compiler find the implementation of the method when I have “used” crate?

For methods defined in traits, you must explicitly import the trait declaration. This means that it is not enough to import a module that implements a trait structurally; you must also import the trait itself.

Why can’t the compiler figure it out for meuseThe statement?

It probably can, but you don’t want it to. While in many cases it is possible for the compiler to determine the correct module to import by simply looking for the definition location of a given identifier, this may not be the case in general. Any decision rules used in RUSTC to select competing options can cause surprise and confusion in some cases, and Rust prefers to be explicit about the origin of the name.

For example, the compiler can say that in the case of competing identifier definitions, it will choose the definition of the earliest imported module. So if module foo and module bar both define the identifier baz, but foo is the first registered module, the compiler inserts use foo::baz; .

mod foo;
mod bar;

// use foo::baz // to be inserted by the compiler.

fn main() {
  baz();
}
Copy the code

Maybe it saves a few keystrokes if you know this is going to happen, but it also greatly increases the likelihood of surprising error messages when you really want to change baz() to bar::baz(), and it reduces the readability of the code by making the meaning of function calls dependent on module declarations. These are compromises we are not willing to make.

However, the IDE can help manage claims, which gives you two benefits: the machine helps pull in names, but explicitly states where those names come from.

How do I do dynamic Rust library loading?

Import the dynamic library in Rust with Libloading, which provides a cross-platform dynamic linking system.

Why does crates. IO have no namespace?

To quote the official explanation for design in Crates. IO:

In the first month of crates. IO, many people asked us if it was possible to introduce namespaces.

While namespaces allow multiple authors to use a single, common name, they add complexity to package references in Rust code and human communication to packages. At first glance, they allow multiple authors to use names like “HTTP”, but that just means that one needs to call these packages “Wycats’ HTTP” or “reem’ HTTP”, which is no benefit over package names like “wycats- HTTP” or “reem- HTTP”.

When we looked at software package ecosystems without namespaces, we found that people tended to use more creative names (such as Nokogiri over Tenderlove’s libxml2). These creative names tend to be short and catchy, in part because they lack any hierarchy. They make it easier to communicate packages succinctly and clearly. They create exciting brands. We have seen the success of some 10,000+ package ecosystems, such as NPM and RubyGems, whose communities thrive within a single namespace.

In short, we don’t think Cargo’s ecosystem would have been better if Piston had chosen a name like BVSSvni/Game-Engine (allowing other users to choose Wycats/Game-Engine) instead of simply Piston.

Because namespaces are technically complex in many ways and are compatible with additions if necessary in the future, we stick with a single shared namespace.

library

How can I make an HTTP request?

The library doesn’t include an implementation of HTTP, so you’ll need to use an external Crate. Reqwest is the simplest. It’s built on Hyper and written in Rust, but there are a few others. Curl Crate is widely used and provides bindings to curl libraries.

How do I write GUI applications with Rust?

There are several ways to write GUI applications in Rust. Just look at this list of GUI frameworks.

How can I parse JSON/XML?

Serde is the recommended library for serializing and deserializing Rust data and is available in many different formats.

Does it have a standard 2D+ vector and shape crate?

Also have no! Want to write one?

How do I write an OpenGL application in Rust?

Glium is the primary library for OpenGL programming in Rust. GLFW is also a solid choice.

Can I write a video game with Rust?

Yes, you can. Rust’s main game programming library is Piston, and there is also a Subreddit for Rust Game programming and an IRC channel (# rust-GameDev on Mozilla IRC).

Design patterns

Is Rust object-oriented?

It’s multimodal. Many of the things you can do in OO languages you can do in Rust, but not all of them, and not always in the abstract way you’re used to.

How do I map object-oriented concepts to Rust?

It depends. There are ways to translate object-oriented concepts, such as multiple inheritance, into Rust, but because Rust is not object-oriented, the results of translation can be quite different from how it looks in OO languages.

How do I handle configuration of structures with optional parameters?

The easiest way to do this is to use the Option type (usually new()) in any function you use to build an instance of the structure. Another approach is to use the builder pattern, where only certain functions that instantiate member variables must be called before the built type can be built.

How do I make global variables in Rust?

Global variables in Rust can use const declarations to implement compile-time global constants, while static can be used to implement mutable global variables. Note that changing the static mut variable requires using unsafe because it allows data contention, which is guaranteed not to happen in safe Rust. An important difference between const and static values is that you can reference static values, but not const values, which have no specified memory location. For more information about const and static, read Rust.

How do I set programmatically defined compile-time constants?

Rust currently has limited support for compile-time constants. You can use a “const” declaration to define primitives (similar to “static” but immutable and with no specified location in memory), or you can define “const” functions and inherent methods.

To define programmatic constants that cannot be defined through these mechanisms, use lazy-static Crate, which simulates compile-time computation by automatically evaluating the constant the first time it is used.

Can I run the initialization code that happens before Main?

Rust has no concept of “life before Main”. The closest is done with lazy-static Crate, which simulates “before main” by lazily initializing static variables when they are first used.

Does Rust allow Globals to use values that are unstructured expressions?

Don’t allow. A global variable cannot have a constructor that is not a structural expression, nor can it have a destructor. Static constructors are not desirable because it is difficult to ensure portability of the static initialization order. Life before Main is generally considered a faulty feature, so Rust does not allow it.

See the “static initialization order fiasco” in C++ FQA, and Eric Lippert’s blog on the challenges of C#, which also has this feature.

You can use the lazy-static toolkit to approximate globals for non-content expressions.

Other languages

How can I implement something like C in Ruststruct X { static int X; };What about the stuff?

Rust does not have the static fields shown in the code snippet above. Instead, you can declare a static variable in a given module that is private to that module.

How do I convert C-style enumerations to integers and vice versa?

Converting c-style enumerations to integers can be done with as expressions, such as E as i64(where e is an enumeration).

The conversion in the other direction can be done with the match statement, which maps different numeric values to different potential values of the enumeration.

Why do Rust programs have a larger binary size than C programs?

There are several factors that cause Rust programs to have a larger binary size by default than comparable C programs. In general, Rust tends to optimize for real-world program performance rather than the size of applets.

Single state

Rust takes a singleton approach to generics, meaning that for every specific type used in a program, a new generic function or type is generated. This is similar to how templates work in C++. For example, in the following program:

fn foo<T>(t: T) {
    // ... do something
}

fn main() {
    foo(10);       // i32
    foo("hello");  // &str
}
Copy the code

Two different versions of Foo will appear in the final binary, one for i32 input and one for &str input. This makes static scheduling of generic functions more efficient, but at the cost of a larger binary.

Debug symbols

Rust programs retain some debug symbols when compiled, even when compiled in release mode. These symbols are used to provide backtrace for Panic and can be removed using strips or other debug symbol removal tools. It is worth noting that compiling in release mode with Cargo is equivalent to setting optimization level 3 with RUSTC. Another optimization level (called S or Z) has been added, which tells the compiler to optimize for size rather than performance.

Link time optimization

Rust does not do link-time optimization by default, but can be instructed to do so. This increases the amount of optimizations that the Rust compiler can make and has a small impact on the size of the binary. Combined with the size optimization patterns mentioned earlier, this effect may be even greater.

The standard library

The Rust standard library includes libbackTrace and libunwind, which may not be desirable in some programs. Therefore, use #! [no_std] can lead to smaller binaries, but it often makes a substantial change to the kind of Rust code you’re writing. Note that using Rust in the absence of a standard library is often functionally closer to equivalent C code.

For example, the following C program reads a name and says “hello” to anyone with that name.

#include <stdio.h>

int main(void) {
    printf("What's your name? \n");
    char input[100] = {0};
    scanf("%s", input);
    printf("Hello %s! \n", input);
    return 0;
}
Copy the code

Rewrite this with Rust and you might get something like this.

use std::io;

fn main() {
    println!("What's your name?");
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    println!("Hello {}!", input);
}
Copy the code

This program compiles with a larger binary and uses more memory than a C program. But this program is not exactly equivalent to the C code above. The equivalent Rust code would look something like this instead.

#! [feature(lang_items)]
#! [feature(libc)]
#! [feature(no_std)]
#! [feature(start)]
#! [no_std]

extern crate libc;

extern "C" {
    fn printf(fmt: *const u8, ...) -> i32;
    fn scanf(fmt: *const u8, ...) -> i32;
}

#[start]
fn start(_argc: isize, _argv: *const *const u8) - >isize {
    unsafe {
        printf(b"What's your name? \n\0".as_ptr());
        let mut input = [0u8; 100];
        scanf(b"%s\0".as_ptr(), &mut input);
        printf(b"Hello %s! \n\0".as_ptr(), &input);
        0}}#[lang="eh_personality"] extern fn eh_personality() {}
#[lang="panic_fmt"] fn panic_fmt() -> ! { loop{}}#[lang="stack_exhausted"] extern fn stack_exhausted() {}
Copy the code

This should really be roughly the same as C in terms of memory usage, but at the cost of more programmer complexity and a lack of the static guarantees typically provided by Rust (avoided here by using unsafe).

Why doesn’t Rust have a stable ABI like C, and why do I have to annotate things with extern?

The commitment to the ABI is a major decision that will limit potentially beneficial language changes in the future. Given that Rust only reached 1.0 in May 2015, it is too early to make big promises like stabilizing the ABI. But that doesn’t mean it won’t happen. (although C++ has been running successfully for many years without specifying a stable ABI).

The extern keyword allows Rust to use a specific ABI, such as a well-defined C ABI, to interoperate with other languages.

Can Rust code call C code?

You can. The design of calling C code from Rust is just as efficient as calling C code from C++.

Can C code call Rust code?

Yes, Rust code must be exposed via an “extern” declaration, which makes it c-ABI compatible. Such a function can be passed to C code as a function pointer, or, if given the #[no_mangle] attribute to disable symbolic entanglement, can be called directly from C code.

I have written perfect C++ code. What can Rust give me?

Modern C++ includes many features that make writing safe and correct code error-proof, but it’s not perfect, and it’s still easy to introduce insecurity. This is something the core developers of C++ are struggling with, but C++ is constrained by a long history that predates many of the ideas they are trying to implement today.

Rust was designed from day one to be a secure system programming language, which means it is not constrained by the historical design decisions that have made C++ security so complicated. In C++, security is achieved through careful personal discipline and is prone to error. In Rust, security is the default. It gives you the ability to work on a team that includes people who are less perfect than you, without having to spend time double-checking their code for security holes.

How can I professionalize the equivalent of C++ templates in Rust?

Rust does not yet have an exact equivalent of template specialization, but it is working on it and hopes to add it soon. However, a similar effect can be achieved with association types.

How does Rust’s ownership system relate to the mobile semantics of C++?

The underlying concepts are similar, but the way the two systems work in practice is very different. In both systems, “move” a value is a way to transfer ownership of its underlying resource. For example, moving a string transfers the buffer of the string, not copying it.

In Rust, transfer of ownership is the default behavior. For example, if I write a function that takes “String” as an argument, the function will have ownership of the String value provided by its caller.

fn process(s: String) {}fn caller() {
    let s = String::from("Hello, world!");
    process(s); // Transfers ownership of `s` to `process`
    process(s); // Error! ownership already transferred.
}
Copy the code

As you can see in the above snippet, the first call to process in the function caller transfers ownership of the variable S. The compiler tracks ownership, so a second call to process results in an error because it is illegal to transfer ownership of the same value twice. Rust also prevents you from moving a value if it has an unfinished reference.

C++ takes a different approach. In C++, the default is to copy a value (more specifically, to call the copy constructor). However, callers can use an “rvalue reference” to declare their arguments, such as String &&, to indicate that they will take ownership of some of the resources owned by that argument (in this case, the internal buffer of the string). The caller must then pass a temporary expression or make an explicit move using STD ::move. The rough equivalent of the above function process is:

void process(string&& s) {}void caller(a) {
    string s("Hello, world!");
    process(std::move(s));
    process(std::move(s));
}
Copy the code

The C++ compiler is not obligated to track movement. For example, the code above compiles without any warnings or errors, at least with the default Settings. In addition, in C++, the ownership of the string s itself (if not its internal buffer) still belongs to the caller, so s’s destructor runs when the caller returns, even if it has been moved (in contrast, in Rust, the moved value is only discarded by its new owner).

How can I interoperate from Rust with C++, or C++ with Rust?

Rust and C++ interoperate through the C language. Both Rust and C++ provide a foreign function interface to C and can be used to communicate with each other. If writing C bindings is too tedious, you can use rust-Bindgen to help automatically generate viable C bindings.

Does Rust have C++ style constructors?

No, functions serve the same function as constructors and do not add complexity to the language. In Rust, the common name for the equivalent constructor is new(), although this is a convention rather than a language rule. The new() function is really just like any other function. An example of it looks like this.

struct Foo {
    a: i32,
    b: f64,
    c: bool,}impl Foo {
    fn new() -> Foo {
        Foo {
            a: 0,
            b: 0.0,
            c: false,}}}Copy the code

Does Rust have a copy constructor?

Not exactly. A type that implements Copy makes a standard C-like “shallow Copy” that requires no extra work (similar to the Trivially copyable type in C++). It is not possible to implement a Copy type that requires custom replication behavior. In Rust, by contrast, the “copy constructor” is created by implementing the Clone feature and explicitly calling the Clone method. Making user-defined copy operators explicit makes it easier for developers to identify potentially expensive operations.

Does Rust have a move constructor?

No. All types of values are moved through memcpy. This makes it easier to write general-purpose unsafe code because assignment, pass and return are known and do not have the side effects of unwinding.

What are the similarities and differences between Go and Rust?

Rust and Go have very different design goals. The following differences are not the only ones (there are too many to list them all), but they are some of the more important ones:

  • Rust has a lower level than Go. For example, Rust does not need a garbage collector, whereas Go does. In general, Rust provides a level of control comparable to C or C++.
  • While Rust focuses on ensuring security and efficiency while providing high-level capabilities, Go focuses on being a small, simple language that can compile quickly and work well with a variety of tools.
  • Rust has strong support for generics, which Go (for now) does not.
  • Rust is strongly influenced by the world of functional programming, including the type system extracted from Haskell’s Typeclasses. Go has a simpler type system, using interfaces for basic generic programming.

How do Rust traits compare to Haskell typeclasses?

Rust traits are similar to Haskell’s typeclasses, but are not yet as powerful because Rust cannot express higher types of types. The association type of Rust is equivalent to the Haskell type family.

Some specific differences between Haskell typeclasses and Rust traits include:

  • Rust traits has an implicit first parameter calledSelf. In the Rusttrait BarCorresponds to Haskellclass Bar selfWhile in Rusttrait Bar<Foo>Corresponds to Haskellclass Bar foo self.
  • “Supertraits” or “superclass constraints” in Rust are written as suchtrait Sub: Super, whereas is in Haskellclass Super self => Sub self.
  • Rust prohibits owner-less instances, which results in different consistency rules in Rust than in Haskell.
  • The RustimplParsing determines twoimplWhether overlapped or in potentialimplWhen making a choice between, will consider the relevantwhereTerm and trait constraints. Haskell only considerinstanceConstraints in declarations, regardless of any constraints provided elsewhere.
  • A subset of Rust’s traits (traits of “object safety”) can be used for dynamic scheduling through trait objects. The same function in Haskell GHC style through the “ExistentialQuantification” is available.

Documentation

Why are so many of Rust’s answers wrong on Stack Overflow?

The Rust language has been around for years, reaching version 1.0 in May 2015. The language has changed a lot in the time before that, and some of the answers to Stack Overflow were given in older versions of the language.

Over time, more and more answers will be provided for the current version, improving the problem as the proportion of outdated answers decreases.

Where do I report problems in Rust documents?

You can report problems in Rust documents on the Rust compiler Issue Tracker. Be sure to read the contribution guide first.

How do I view Rustdoc documents for libraries that my project depends on?

When you use Cargo Doc to generate documentation for your own projects, it also generates documentation for dependent versions of activities. These documents will be placed in the target/doc directory of your project. Open these documents using cargo Doc –open, or open target/doc/index.html yourself.