24 days from Node.js to Rust

preface

Through the previous 18 tutorials, we’ve learned how to set up a development environment, we’ve learned Rustup, VS Code, rust-Analyzer, we’ve gone through the phase of being a beginner, now we need to start learning how to build a real project

The body of the

Set up the workspace

I suggest you use Cargo’s workspace, Rust and JavaScript, all of which are logical functions that are better managed when modularized. We will now start with an executable library. We will start with a library and encapsulate it with a command line program

First, create a workspace in an empty folder and add the following to Cargo. Toml:

[workspace]
members = ["crates/*"]
Copy the code

Members lists all crate in the workspace, which is configured to include everything in the Crates folder

Establish a library

We create a new library with cargo New:

$ cargo new --lib crates/my-lib
Copy the code

The differences between Binary Crate and Library Crate are small. By default, Binary Crate has a main.rs while Library Crate has a lib.rs. Cargo new –lib also adds cargo. Lock to.gitignore

Cargo Book recommends keeping Cargo. Lock on the production side (binary, server, microservice, etc.) but omitting it in the Library

The default content for lib.rs is simple:

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        let result = 2 + 2;
        assert_eq!(result, 4); }}Copy the code

You’ll see about unit testing here. Rust has unit testing built in, and does not require additional configuration of the unit testing framework or how to run the tests

This means that the unit tests are in your source code. Rust also has integration tests, which are stored in the tests folder at the same level as the SRC folder, but these can only test public interfaces. If you want private code, you can only write tests in the source code

Unit testing

Library templates introduce two new properties: #[CFG ()] and #[test]

[# CFG ()] is used for conditional compilation, specifying #[CFG (test)] in front of a module means Rust will ignore the compilation of the module unless it has the test flag

#[cfg(test)]
mod tests {
}
Copy the code

[#test] will edit a function into a unit test, and Rust will execute them one by one when you run cargo test

$ cargo test
running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests my-lib

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in0.00 sCopy the code

The assertions provided by Rust are basic, and you can use Assert! (), assert_eq! () or assert_ne! () to make a judgment

Writing test cases is a good place to start to determine the functionality of an API, and while strict TDD is a bit extreme, designing tests before writing an API forces you to think about the implementation.

WebAssembly is hot right now, so let’s create a WASM Runner. Let’s change lib.rs to something like this:

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn loads_wasm_file() {
        let result = Module::from_path("./tests/test.wasm");
        assert!(result.is_ok()); }}Copy the code

Adding use super::* allows us to use all the content of the parent module without the prefix

The module structure does not yet exist, but we expect to need a way to load the file from the local directory. The loading may fail, so the return value will be Result

We could run cargo test and get a compilation error because some functionality is not yet implemented

error[E0433]: failed to resolve: use of undeclared type `Module`
 --> crates/wasm-runner/src/lib.rs:6:22
  |
6 |         let result = Module::from_file("./tests/test.wasm");
  |                      ^^^^^^ use of undeclared type `Module`
For more information about this error, try `rustc --explain E0433`.
Copy the code

We need to add the Module structure as well as the from_file function, to which we passed an &str in the test, but we want the argument to be anything that represents a path

use std::path::Path;
struct Module {}

impl Module {
    fn from_file<T: AsRef<Path>>(path: T) -> Result<SelfAnd????? > {Ok(Self{})}}Copy the code

Now we need to specify the type of Error that will be returned, because we are loading from the file system and these methods will return IO ::Error, so we can do that now as well

Now our code is ready to run, although we haven’t done anything useful yet

use std::path::Path;
struct Module {}

impl Module {
    fn from_file<T: AsRef<Path>>(path: T) -> Result<Self, std::io::Error> {
        Ok(Self{})}}#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn executes_wasm_file() {
        let result = Module::from_file("./tests/test.wasm");
        assert!(result.is_ok()); }}Copy the code

Create a command line program

Create a command line program using cargo new Crates /[your cli name], which is $cargo new Crates /cli, and add a dependency to cargo. Toml:

[dependencies]
my-lib = { path = ".. /my-lib" }
Copy the code

We are now ready to import the code from the my_lib namespace

Rust has a rule that allows a hyphen in Crate names, but not in Rust identifiers, so if your Crate name uses a hyphen, use an underscore instead

use my_lib::Module;
Copy the code

VS Code will give you an error when entering the above Code

This is because the Module does not explicitly say that it is public, so we cannot introduce it. Now we add pub to struct Module and fn from_file I

pub struct Module {}

impl Module {
    pub fn from_file<T: AsRef<Path>>(path: T) -> Result<Self, std::io::Error> {
        Ok(Self{})}}Copy the code

Now we can introduce the Module in the command line program and call the Module::from_file method

use my_lib::Module;

fn main() {
  match Module::from_file("./module.wasm") {
    Ok(_) = > {println!("Module loaded");
    }
    Err(e) => {
      println!("Module failed to load: {}", e); }}}Copy the code

Run the command line program

We can run our command line programs using cargo Run in the./crates/ CLI folder, but Cargo can also run commands in any of the sub-Crate -p flags, in our project root path, Cargo Run -p cli to run the default binaries in CRATE

$ cargo run -p cli
Module loaded
Copy the code

There is still a lot to do, but so far we have laid a solid foundation

reading

  • Rust by Example: Unit testing
  • Rust Book, 11.01: How to Write Tests
  • Rust Book, 14.03: Cargo Workspaces
  • How to Structure Unit Tests in Rust

conclusion

It’s important to lay a solid foundation. Often you’ll be at a loss as to what best practices are in the beginning, which can shake your confidence. But when you get past those moments, everything feels so natural that you forget the agony of learning at first

More and more

  • Rust tutorial (1) From NVM to Rust
  • Rust tutorial (2) from NPM to Cargo for the front end
  • Configure Visual Studio Code in Rust (3)
  • Rust tutorial (4) Hello World
  • Rust Tutorial for the front end (5) Borrowing & Ownership
  • Part 1 of Rust Tutorial (6) String
  • Rust Tutorial (7)
  • Rust Tutorial (8)
  • Rust Tutorial (9)
  • Rust tutorial for the front end (10) from Mixins to Traits
  • Rust Tutorial (11) Module for the front-end
  • Part 2 of Rust Tutorial (12) String
  • Rust Tutorial (13) Results & Options
  • Rust tutorial (14) Errors
  • Rust Tutorial (15) closures for the front end
  • Rust Tutorial for the front-end (16) Lifecycle
  • Rust Tutorial (17) iteration for the front end
  • Rust tutorial for the front-end (18) Asynchrony
  • Part 1 of the Rust Tutorial (19) For the front end
  • Part 2 of Rust Tutorial (20) For the front End
  • Part 3 of Rust Tutorial (21) For the front End