├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .vscode
└── tasks.json
├── README.md
├── book.toml
├── examples
└── serde
│ ├── bincode
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
│ └── feature
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── img
├── README
├── vscode_create.png
├── vscode_port.png
├── vscode_run.png
└── vscode_terminal.png
└── src
├── SUMMARY.md
├── index.md
├── lazy_static.md
├── ndarray.md
├── openssl.md
├── openssl
└── rsa.md
├── rand.md
├── rbeext.gif
├── regex.md
├── serde.md
├── serde
├── bincode.md
├── feature.md
└── json.md
├── webassembly.md
└── webassembly
├── browser.md
├── compile.md
├── nodejs.md
├── nodejshelper.md
└── run.md
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------------------------------------------------------------------
2 | # Copyright (c) Microsoft Corporation. All rights reserved.
3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
4 | #-------------------------------------------------------------------------------------------------------------
5 |
6 | FROM rust:1
7 |
8 | # This Dockerfile adds a non-root user with sudo access. Use the "remoteUser"
9 | # property in devcontainer.json to use it. On Linux, the container user's GID/UIDs
10 | # will be updated to match your local UID/GID (when using the dockerFile property).
11 | # See https://aka.ms/vscode-remote/containers/non-root-user for details.
12 | ARG USERNAME=vscode
13 | ARG USER_UID=1000
14 | ARG USER_GID=$USER_UID
15 |
16 | # Avoid warnings by switching to noninteractive
17 | ENV DEBIAN_FRONTEND=noninteractive
18 |
19 | # Configure apt and install packages
20 | RUN apt-get update \
21 | && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \
22 | #
23 | # Verify git, needed tools installed
24 | && apt-get -y install git openssh-client less iproute2 procps lsb-release \
25 | #
26 | # Install lldb, vadimcn.vscode-lldb VSCode extension dependencies
27 | && apt-get install -y lldb python3-minimal libpython3.7 \
28 | #
29 | # Install Rust components
30 | && rustup update 2>&1 \
31 | && rustup component add rls rust-analysis rust-src rustfmt clippy 2>&1 \
32 | #
33 | # Install mdbook
34 | && cargo install mdbook 2>&1 \
35 | #
36 | # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user.
37 | && groupadd --gid $USER_GID $USERNAME \
38 | && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
39 | # [Optional] Add sudo support for the non-root user
40 | && apt-get install -y sudo \
41 | && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
42 | && chmod 0440 /etc/sudoers.d/$USERNAME \
43 | #
44 | # Clean up
45 | && apt-get autoremove -y \
46 | && apt-get clean -y \
47 | && rm -rf /var/lib/apt/lists/*
48 |
49 | # Switch back to dialog for any ad-hoc use of apt-get
50 | ENV DEBIAN_FRONTEND=dialog
51 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Rust",
3 | "dockerFile": "Dockerfile",
4 | "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],
5 |
6 | // Set *default* container specific settings.json values on container create.
7 | "settings": {
8 | "terminal.integrated.shell.linux": "/bin/bash",
9 | "lldb.executable": "/usr/bin/lldb"
10 | },
11 |
12 | // This is the default port for mdbook serve
13 | "forwardPorts": [3000],
14 |
15 | // Add the IDs of extensions you want installed when the container is created.
16 | "extensions": [
17 | "rust-lang.rust",
18 | "bungcip.better-toml",
19 | "yzhang.markdown-all-in-one",
20 | "streetsidesoftware.code-spell-checker",
21 | "shd101wyy.markdown-preview-enhanced",
22 | "bierner.github-markdown-preview"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 |
17 | - name: Install mdbook
18 | run: cargo install mdbook
19 |
20 | - name: Build
21 | run: mdbook build
22 |
23 | - name: Publish
24 | uses: peaceiris/actions-gh-pages@v3
25 | with:
26 | github_token: ${{ secrets.GITHUB_TOKEN }}
27 | publish_dir: ./book
28 | cname: rust-by-example-ext.com
29 |
30 | - name: Upload Artifact
31 | uses: actions/upload-artifact@v1
32 | with:
33 | name: rust-by-example-ext
34 | path: book
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
7 | Cargo.lock
8 |
9 | # These are backup files generated by rustfmt
10 | **/*.rs.bk
11 |
12 | # mdbook target
13 | book
14 |
15 | # Auto-generated files from macOS
16 | .DS_Store
17 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [{
4 | "label": "mdbook serve",
5 | "type": "shell",
6 | "command": "mdbook",
7 | "args": ["serve"],
8 | "problemMatcher": []
9 | },{
10 | "label": "mdbook build",
11 | "type": "shell",
12 | "command": "mdbook",
13 | "args": ["build"],
14 | "problemMatcher": []
15 | }]
16 | }
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rust By Example -- Extended Edition
2 |
3 | 
4 |
5 | Learn Rust with examples from 3rd party crates. This book is the sister book for the [Rust By Example](https://doc.rust-lang.org/rust-by-example/) book,
6 | which covers core Rust language features and standard libraries. This book, being the Extended Edition, focuses on commonly
7 | used 3rd party libraries and tools.
8 |
9 | ## Using
10 |
11 | If you'd like to read Rust by Example -- Extended Edition, you can visit https://rust-by-example-ext.com/
12 | to read it online.
13 |
14 | ## Interactive code example
15 |
16 | 
17 |
18 | ## Contribute using VSCode codespaces
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | The VSCode online IDE allows you to make changes, test your changes, and send us Pull Requests without having to install any software on your computer!
27 |
28 | * First [fork this repository](https://github.com/second-state/rust-by-example-ext/fork)!
29 | * Log into [VSCode codespaces](https://online.visualstudio.com/) with your Azure account and [open the forked repository in a new codespace](img/vscode_create.png). It will take a few minutes as codespace needs to build `mdbook` in the process.
30 | * Make changes or add to MD files in VSCode.
31 | * Use [`Menu | Terminal | Run Task ... | mdbook server`](img/vscode_run.png) to start a local server for your book. You will see it running in the [IDE's built-in Terminal](img/vscode_terminal.png).
32 | * [Open a browser](img/vscode_port.png) to access the VSCode server to see the book, and confirm your edits look good.
33 | * Use the Git menu to commit the changes, and use the GitHub menu to send the changes to use via Pull Request.
34 |
35 | ## Building locally
36 |
37 | If you'd like to read it locally, [install Rust], and then:
38 |
39 | ```bash
40 | $ git clone https://github.com/second-state/rust-by-example-ext
41 | $ cd rust-by-example-ext
42 | $ cargo install mdbook
43 | $ mdbook build
44 | $ mdbook serve
45 | ```
46 |
47 | [install Rust]: https://www.rust-lang.org/tools/install
48 |
49 | ## License
50 |
51 | Rust by Example is licensed under either of
52 |
53 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
54 | * [MIT license](http://opensource.org/licenses/MIT)
55 |
56 | at your option.
57 |
58 | Unless you explicitly state otherwise, any contribution intentionally submitted
59 | for inclusion in Rust by Example -- Extended Edition by you, as defined in the Apache-2.0 license, shall be
60 | dual licensed as above, without any additional terms or conditions.
61 |
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | title = "Rust By Example -- Extended Edition"
3 | description = "Covers popular third party Rust crates and libraries by example"
4 | author = ["Second State", "Michael Yuan"]
5 |
6 | [output.html]
7 | google-analytics = "UA-137134458-10"
8 | git-repository-url = "https://github.com/second-state/rust-by-example-ext"
9 | git-repository-icon = "fa-github"
10 |
11 | [output.html.playpen]
12 | editable = true
13 | editor = "ace"
14 |
15 | [output.html.fold]
16 | enable = true
17 |
18 | [output.html.search]
19 | enable = true
20 | limit-results = 30
21 | teaser-word-count = 30
22 | use-boolean-and = true
23 | boost-title = 2
24 | boost-hierarchy = 1
25 | boost-paragraph = 1
26 | expand = true
27 | heading-split-level = 3
28 | copy-js = true
29 |
--------------------------------------------------------------------------------
/examples/serde/bincode/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | [[package]]
4 | name = "bincode"
5 | version = "1.2.1"
6 | source = "registry+https://github.com/rust-lang/crates.io-index"
7 | checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf"
8 | dependencies = [
9 | "byteorder",
10 | "serde",
11 | ]
12 |
13 | [[package]]
14 | name = "byteorder"
15 | version = "1.3.4"
16 | source = "registry+https://github.com/rust-lang/crates.io-index"
17 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
18 |
19 | [[package]]
20 | name = "proc-macro2"
21 | version = "1.0.10"
22 | source = "registry+https://github.com/rust-lang/crates.io-index"
23 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
24 | dependencies = [
25 | "unicode-xid",
26 | ]
27 |
28 | [[package]]
29 | name = "quote"
30 | version = "1.0.3"
31 | source = "registry+https://github.com/rust-lang/crates.io-index"
32 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
33 | dependencies = [
34 | "proc-macro2",
35 | ]
36 |
37 | [[package]]
38 | name = "rbeext_serde_bincode"
39 | version = "0.1.0"
40 | dependencies = [
41 | "bincode",
42 | "serde",
43 | ]
44 |
45 | [[package]]
46 | name = "serde"
47 | version = "1.0.106"
48 | source = "registry+https://github.com/rust-lang/crates.io-index"
49 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
50 | dependencies = [
51 | "serde_derive",
52 | ]
53 |
54 | [[package]]
55 | name = "serde_derive"
56 | version = "1.0.106"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
59 | dependencies = [
60 | "proc-macro2",
61 | "quote",
62 | "syn",
63 | ]
64 |
65 | [[package]]
66 | name = "syn"
67 | version = "1.0.17"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
70 | dependencies = [
71 | "proc-macro2",
72 | "quote",
73 | "unicode-xid",
74 | ]
75 |
76 | [[package]]
77 | name = "unicode-xid"
78 | version = "0.2.0"
79 | source = "registry+https://github.com/rust-lang/crates.io-index"
80 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
81 |
--------------------------------------------------------------------------------
/examples/serde/bincode/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rbeext_serde_bincode"
3 | version = "0.1.0"
4 | authors = ["Michael Yuan"]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | serde = { version = "1.0", features = ["derive"] }
9 | bincode = "1.2.1"
10 |
--------------------------------------------------------------------------------
/examples/serde/bincode/src/main.rs:
--------------------------------------------------------------------------------
1 | use serde::{Serialize, Deserialize};
2 | use bincode;
3 |
4 | #[derive(Serialize, Deserialize, Debug)]
5 | struct Point {
6 | x: f32,
7 | y: f32
8 | }
9 |
10 | #[derive(Serialize, Deserialize, Debug)]
11 | struct Line {
12 | points: Vec,
13 | valid: bool,
14 | length: f32,
15 | desc: String
16 | }
17 |
18 | fn main() {
19 | let x: i32 = 5;
20 | let xs: Vec = bincode::serialize(&x).unwrap();
21 | println!("i32 number {} serializes into byte array {:?}", x, xs);
22 | let xd: i32 = bincode::deserialize(&xs).unwrap();
23 | assert_eq!(x, xd);
24 |
25 | let x: f32 = 3.14;
26 | let xs = bincode::serialize(&x).unwrap();
27 | println!("f32 number {} serializes into byte array {:?}", x, xs);
28 |
29 | let x: Vec = vec![1, 2, 3];
30 | let xs = bincode::serialize(&x).unwrap();
31 | println!("Vec {:?} serializes into byte array {:?}", x, xs);
32 | let xd: Vec = bincode::deserialize(&xs).unwrap();
33 | assert_eq!(x, xd);
34 |
35 | let x: Vec = vec![3.141, 2.718, 1.618];
36 | let xs = bincode::serialize(&x).unwrap();
37 | println!("Vec {:?} serializes into byte array {:?}", x, xs);
38 | let xd: Vec = bincode::deserialize(&xs).unwrap();
39 | assert_eq!(x, xd);
40 |
41 | let x: (i32, &str, f32, bool) = (1, "hello", 4.5, true);
42 | let xs = bincode::serialize(&x).unwrap();
43 | println!("tuple {:?} serializes into byte array {:?}", x, xs);
44 | let xd: (i32, &str, f32, bool) = bincode::deserialize(&xs).unwrap();
45 | assert_eq!(x, xd);
46 |
47 | let x = ((1u8, 2u16), (3.141f32, 'a'), true);
48 | let xs = bincode::serialize(&x).unwrap();
49 | println!("nested tuple {:?} serializes into byte array {:?}", x, xs);
50 |
51 | let point1: Point = Point {x:1.0, y:2.0};
52 | let point2: Point = Point {x:3.0, y:4.0};
53 | let point1s = bincode::serialize(&point1).unwrap();
54 | let point2s = bincode::serialize(&point2).unwrap();
55 | println!("struct Point serializes into byte array {:?}", point1s);
56 | println!("struct Point serializes into byte array {:?}", point2s);
57 |
58 | let length = ((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)).sqrt();
59 | let valid = if length == 0.0 { false } else { true };
60 | let line = Line { points: vec![point1, point2], valid: valid, length: length, desc: "a thin line".to_string() };
61 | let lines = bincode::serialize(&line).unwrap();
62 | println!("struct Line serializes into byte array {:?}", lines);
63 |
64 | let lined: Line = bincode::deserialize(&lines).unwrap();
65 | assert_eq!(lined.desc, "a thin line");
66 | assert_eq!(lined.points[1].x, 3.0);
67 | }
68 |
--------------------------------------------------------------------------------
/examples/serde/feature/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | [[package]]
4 | name = "autocfg"
5 | version = "1.0.0"
6 | source = "registry+https://github.com/rust-lang/crates.io-index"
7 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
8 |
9 | [[package]]
10 | name = "itoa"
11 | version = "0.4.5"
12 | source = "registry+https://github.com/rust-lang/crates.io-index"
13 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
14 |
15 | [[package]]
16 | name = "num"
17 | version = "0.2.1"
18 | source = "registry+https://github.com/rust-lang/crates.io-index"
19 | checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36"
20 | dependencies = [
21 | "num-bigint",
22 | "num-complex",
23 | "num-integer",
24 | "num-iter",
25 | "num-rational",
26 | "num-traits",
27 | ]
28 |
29 | [[package]]
30 | name = "num-bigint"
31 | version = "0.2.6"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
34 | dependencies = [
35 | "autocfg",
36 | "num-integer",
37 | "num-traits",
38 | "serde",
39 | ]
40 |
41 | [[package]]
42 | name = "num-complex"
43 | version = "0.2.4"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
46 | dependencies = [
47 | "autocfg",
48 | "num-traits",
49 | ]
50 |
51 | [[package]]
52 | name = "num-integer"
53 | version = "0.1.42"
54 | source = "registry+https://github.com/rust-lang/crates.io-index"
55 | checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
56 | dependencies = [
57 | "autocfg",
58 | "num-traits",
59 | ]
60 |
61 | [[package]]
62 | name = "num-iter"
63 | version = "0.1.40"
64 | source = "registry+https://github.com/rust-lang/crates.io-index"
65 | checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
66 | dependencies = [
67 | "autocfg",
68 | "num-integer",
69 | "num-traits",
70 | ]
71 |
72 | [[package]]
73 | name = "num-rational"
74 | version = "0.2.4"
75 | source = "registry+https://github.com/rust-lang/crates.io-index"
76 | checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
77 | dependencies = [
78 | "autocfg",
79 | "num-bigint",
80 | "num-integer",
81 | "num-traits",
82 | ]
83 |
84 | [[package]]
85 | name = "num-traits"
86 | version = "0.2.11"
87 | source = "registry+https://github.com/rust-lang/crates.io-index"
88 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
89 | dependencies = [
90 | "autocfg",
91 | ]
92 |
93 | [[package]]
94 | name = "proc-macro2"
95 | version = "1.0.10"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
98 | dependencies = [
99 | "unicode-xid",
100 | ]
101 |
102 | [[package]]
103 | name = "quote"
104 | version = "1.0.3"
105 | source = "registry+https://github.com/rust-lang/crates.io-index"
106 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
107 | dependencies = [
108 | "proc-macro2",
109 | ]
110 |
111 | [[package]]
112 | name = "rbeext_serde_feature"
113 | version = "0.1.0"
114 | dependencies = [
115 | "num",
116 | "num-bigint",
117 | "serde",
118 | "serde_json",
119 | ]
120 |
121 | [[package]]
122 | name = "ryu"
123 | version = "1.0.3"
124 | source = "registry+https://github.com/rust-lang/crates.io-index"
125 | checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
126 |
127 | [[package]]
128 | name = "serde"
129 | version = "1.0.106"
130 | source = "registry+https://github.com/rust-lang/crates.io-index"
131 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
132 | dependencies = [
133 | "serde_derive",
134 | ]
135 |
136 | [[package]]
137 | name = "serde_derive"
138 | version = "1.0.106"
139 | source = "registry+https://github.com/rust-lang/crates.io-index"
140 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
141 | dependencies = [
142 | "proc-macro2",
143 | "quote",
144 | "syn",
145 | ]
146 |
147 | [[package]]
148 | name = "serde_json"
149 | version = "1.0.51"
150 | source = "registry+https://github.com/rust-lang/crates.io-index"
151 | checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
152 | dependencies = [
153 | "itoa",
154 | "ryu",
155 | "serde",
156 | ]
157 |
158 | [[package]]
159 | name = "syn"
160 | version = "1.0.17"
161 | source = "registry+https://github.com/rust-lang/crates.io-index"
162 | checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
163 | dependencies = [
164 | "proc-macro2",
165 | "quote",
166 | "unicode-xid",
167 | ]
168 |
169 | [[package]]
170 | name = "unicode-xid"
171 | version = "0.2.0"
172 | source = "registry+https://github.com/rust-lang/crates.io-index"
173 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
174 |
--------------------------------------------------------------------------------
/examples/serde/feature/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rbeext_serde_feature"
3 | version = "0.1.0"
4 | authors = ["Michael Yuan"]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | serde = { version = "1.0", features = ["derive"] }
9 | serde_json = "1.0"
10 | num-bigint = { version = "0.2", features = ["serde"] }
11 | num = "0.2"
12 |
--------------------------------------------------------------------------------
/examples/serde/feature/src/main.rs:
--------------------------------------------------------------------------------
1 | use num::bigint::ToBigInt;
2 | use num_bigint::BigInt;
3 |
4 | fn main() {
5 | let a = 3.to_bigint().unwrap();
6 | let x = num::pow(a, 247);
7 | let xs = serde_json::to_string(&x).unwrap();
8 | let xd: BigInt = serde_json::from_str(&xs).unwrap();
9 | assert_eq!(x, xd);
10 |
11 | println!("3**247 is {} and serializes to {}", x, xs);
12 | }
13 |
--------------------------------------------------------------------------------
/img/README:
--------------------------------------------------------------------------------
1 | README
2 |
--------------------------------------------------------------------------------
/img/vscode_create.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/second-state/rust-by-example-ext/b3821ad2ff05b07b6b79c9d81c4819f327e58d83/img/vscode_create.png
--------------------------------------------------------------------------------
/img/vscode_port.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/second-state/rust-by-example-ext/b3821ad2ff05b07b6b79c9d81c4819f327e58d83/img/vscode_port.png
--------------------------------------------------------------------------------
/img/vscode_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/second-state/rust-by-example-ext/b3821ad2ff05b07b6b79c9d81c4819f327e58d83/img/vscode_run.png
--------------------------------------------------------------------------------
/img/vscode_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/second-state/rust-by-example-ext/b3821ad2ff05b07b6b79c9d81c4819f327e58d83/img/vscode_terminal.png
--------------------------------------------------------------------------------
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | [Introduction](index.md)
4 |
5 | - [Serialization](serde.md)
6 | - [JSON](serde/json.md)
7 | - [Binary](serde/bincode.md)
8 | - [Third party crates](serde/feature.md)
9 |
10 | - [Random numbers](rand.md)
11 |
12 | - [SSL/TLS toolkit](openssl.md)
13 | - [RSA](openssl/rsa.md)
14 |
15 | - [N-dimensional array](ndarray.md)
16 |
17 | - [Lazy initialization](lazy_static.md)
18 |
19 | - [Regular expression](regex.md)
20 |
21 | - [WebAssembly](webassembly.md)
22 | - [Compile targets](webassembly/compile.md)
23 | - [Run a simple program](webassembly/run.md)
24 | - [Rust Javascript binding](webassembly/browser.md)
25 | - [WebAssembly on the server side](webassembly/nodejs.md)
26 | - [Call Javascript from Rust](webassembly/nodejshelper.md)
27 |
28 |
--------------------------------------------------------------------------------
/src/index.md:
--------------------------------------------------------------------------------
1 | # Rust by Example -- Extended Edition
2 |
3 | [Rust][rust] is a modern systems programming language focusing on safety, speed,
4 | and concurrency. It accomplishes these goals by being memory safe without using
5 | garbage collection. A big part of Rust's strength and success comes from the
6 | large ecosystem of third party libraries, known as crates.
7 |
8 | Rust by Example -- Extended Edition (RBEext) is a collection of runnable examples that illustrate how to use popular Rust
9 | third party libraries and crates. It is designed to complement the official
10 | [Rust by Example (RBE)][rbe] book that focuses on the
11 | core language and standard libraries.
12 | Additionally for the curious, you can also [check out the source code for this site][home].
13 |
14 | > **Note:** Many examples in this book are directly runnable from the web page. See the animation below this note. However, the [Rust playground](https://play.rust-lang.org/) only supports [100](https://github.com/integer32llc/rust-playground/blob/master/compiler/base/Cargo.toml) 3rd party crates. For crates or features that are not supported by the playground, we show the results next to the examples, and link to [cargo project source code](https://github.com/second-state/rust-by-example-ext/tree/master/examples/).
15 |
16 | 
17 |
18 | Now let's begin!
19 |
20 | - [Serialization](serde.md) - Serialization and deserialization are key to data exchange between Rust programs and the rest of the world. The `serde` crate is the de facto standard here.
21 |
22 | - [Random numbers](rand.md) - It is surprisingly difficult to get high quality random numbers for your application. The `rand` crate can help.
23 |
24 | - [SSL/TLS toolkit](openssl.md) - Rust API wrappers for the OpenSSL library to handle public key infrastructure and secure communications. Encryption, decryption, digital certificates, digital signature, secure digest, secure network protocols, and more!
25 |
26 | - [N-dimensional array](ndarray.md) - Multi-dimensional arrays are crucial for scientific computing, data mining, machine learning (ML), and artificial intelligence (AI) applications.
27 |
28 | - [Lazy initialization](lazy_static.md) - Lazy initialization allows you to assign values to static variables at runtime.
29 |
30 | - [Regular expression](regex.md) - Processing and manipulating text and string values.
31 |
32 | - [WebAssembly](webassembly.md) - It is very popular to run Rust apps in WebAssembly, learn why and how.
33 |
34 | [rust]: https://www.rust-lang.org/
35 | [rbe]: https://doc.rust-lang.org/rust-by-example/
36 | [home]: https://github.com/second-state/rust-by-example-ext
37 |
--------------------------------------------------------------------------------
/src/lazy_static.md:
--------------------------------------------------------------------------------
1 | # Lazy static
2 |
3 | The [`lazy_static`](https://docs.rs/lazy_static/1.4.0/lazy_static/) crate is a simple but widely used macro that allows you to initialize static variables at runtime.
4 | To use it, add the following to your `Cargo.toml`.
5 |
6 | ```
7 | [dependencies]
8 | lazy_static = "1.4.0"
9 | ```
10 |
11 | The example below shows three static variables that must be initialized
12 | by code after the program starts.
13 |
14 | ```rust,editable
15 | #[macro_use]
16 | # extern crate lazy_static;
17 |
18 | use std::collections::HashMap;
19 |
20 | lazy_static! {
21 | static ref HASHMAP: HashMap = {
22 | let mut m = HashMap::new();
23 | m.insert(0, "foo");
24 | m.insert(1, "bar");
25 | m.insert(2, "baz");
26 | m
27 | };
28 | static ref COUNT: usize = HASHMAP.len();
29 | static ref NUMBER: u32 = times_two(21);
30 | }
31 |
32 | fn times_two(n: u32) -> u32 { n * 2 }
33 |
34 | fn main() {
35 | println!("The map has {} entries.", *COUNT);
36 | println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());
37 | println!("A expensive calculation on a static results in: {}.", *NUMBER);
38 | }
39 | ```
40 |
41 |
--------------------------------------------------------------------------------
/src/ndarray.md:
--------------------------------------------------------------------------------
1 | # N-dimensional array
2 |
3 | The N-dimensional array is a widely used data structure for scientific
4 | computing and data analysis. The [`ndarray`](https://crates.io/crates/ndarray) crate provides support for
5 | N-dimensional array in Rust. It is widely used by other crates.
6 | To use the `ndarray` crate, just do the following in your `Cargo.toml` file.
7 |
8 | ```
9 | [dependencies]
10 | ndarray = "0.13.0"
11 | ```
12 |
13 | ## Basic operations
14 |
15 | To create a 3-D array, and access one of its element by index, do the following.
16 | The example creates a 3x4x5 array, and we access its elements using the
17 | `[[i, j, k]]` notation, where `i j k` are index positions for the element.
18 |
19 | ```rust,editable
20 | # extern crate ndarray;
21 |
22 | use ndarray::Array3;
23 |
24 | fn main() {
25 | let mut a3 = Array3::::zeros((3, 4, 5));
26 | a3[[0, 0, 0]] = 0.0;
27 | a3[[1, 1, 1]] = 1.0;
28 | a3[[2, 2, 2]] = 2.0;
29 | println!("The 3D array is {:?}", a3);
30 | }
31 | ```
32 |
33 | Here is another example of a 3x3 2D array.
34 |
35 | ```rust,editable
36 | # extern crate ndarray;
37 |
38 | use ndarray::Array2;
39 |
40 | fn main() {
41 | let mut a2 = Array2::::zeros((3, 3));
42 | a2[[0, 0]] = 0.0;
43 | a2[[0, 1]] = 0.5;
44 | a2[[0, 2]] = -0.5;
45 | a2[[1, 0]] = 1.0;
46 | a2[[1, 1]] = 1.5;
47 | a2[[1, 2]] = -1.5;
48 | a2[[2, 0]] = 2.0;
49 | a2[[2, 1]] = 2.5;
50 | a2[[2, 2]] = -2.5;
51 | println!("The 2D array is {:?}", a2);
52 | }
53 | ```
54 |
55 | Manually setting array elements by index positions is tedious. Next, let's see a better way to do this.
56 |
57 | ## Create arrays
58 |
59 | The `arr2` and `arr3` macros allow you to create 2D and 3D arrays quickly.
60 |
61 | The example creates a 3x3 2D array.
62 |
63 | - The array has 3 rows `[row0, row1, row2]`. For example, `row0` is `[1, 2, 3]`.
64 | - Each row has 3 columns `col0, col1, col2`. For example, `row0 col0` is `1`.
65 |
66 | ```rust,editable
67 | # extern crate ndarray;
68 |
69 | use ndarray::arr2;
70 |
71 | fn main() {
72 | let mut a2 = arr2(&[[1, 2, 3],
73 | [4, 5, 6],
74 | [7, 8, 9]]);
75 | a2[[2, 1]] = 10;
76 | println!("The 2D array is {:?}", a2);
77 | }
78 | ```
79 |
80 | The example creates a 3x2x2 3D array.
81 |
82 | - The array has 3 rows `[row0, row1, row2]`. For example, `row0` is `[[1, 2], [3, 4]]`.
83 | - Each row has 2 columns `[col0, col1]`. For example, `row0 col0` is `[1, 2]`.
84 | - Each column has 2 levels `lvl0, lvl1`. For example, `row0 col0 lvl0` is `1`.
85 |
86 | ```rust,editable
87 | # extern crate ndarray;
88 |
89 | use ndarray::arr3;
90 |
91 | fn main() {
92 | let mut a3 = arr3(&[[[1, 2], [3, 4]],
93 | [[5, 6], [7, 8]],
94 | [[9, 0], [1, 2]]]);
95 | a3[[2, 1, 1]] = 10;
96 | println!("The 3D array is {:?}", a3);
97 | }
98 | ```
99 |
100 | ## Iterate through the arrays
101 |
102 | Using the `genrows()` function, you can flatten the `n` dimension array into an
103 | array of rows. Each row contains a simple (one dimension) array of values
104 | along the original array's shortest axis.
105 |
106 | Using the `outer_iter()` function, you can deconstruct the `n` dimension array
107 | into a simple (one dimension) array of `n-1` dimension arrays.
108 |
109 | ```rust,editable
110 | # extern crate ndarray;
111 |
112 | use ndarray::arr3;
113 |
114 | fn main() {
115 | let a3 = arr3(&[[[1, 2], [3, 4]],
116 | [[5, 6], [7, 8]],
117 | [[9, 0], [1, 2]]]);
118 | for row in a3.genrows() {
119 | // Each row is a 1D array
120 | println!("row is {:?}", row);
121 | }
122 | for a2 in a3.outer_iter() {
123 | println!("2D array is {:?}", a2);
124 | }
125 | }
126 | ```
127 |
--------------------------------------------------------------------------------
/src/openssl.md:
--------------------------------------------------------------------------------
1 | # Cryptography with openssl
2 |
3 | [OpenSSL](https://www.openssl.org/) is an widely used open source library to perform cryptographic
4 | operations. We can use it to encrypt, decrypt, digest, and sign data,
5 | as well as to make SSL/TLS secure connections through the Internet.
6 |
7 | In this chapter, we will cover the [`openssl`](https://crates.io/crates/openssl) crate.
8 | You can [read more documentation about it](https://docs.rs/openssl/0.10.29/openssl/).
9 |
10 | > Since the `openssl` crate relies on the native operating system's OpenSSL library to perform cryptographic operations, it cannot be compiled into WebAssembly targets.
11 |
12 | Let's [start with an example](openssl/rsa.md) to create a pair of RSA keys, and use them to encrypt and decrypt data.
13 |
14 |
--------------------------------------------------------------------------------
/src/openssl/rsa.md:
--------------------------------------------------------------------------------
1 | # RSA public key encryption
2 |
3 | To use the `openssl` crate, you just need to add the following dependencies to your `Cargo.toml` file.
4 |
5 | ```
6 | [dependencies]
7 | openssl = "0.10.28"
8 | ```
9 |
10 | The example below generates an RSA public and private key pair, and
11 | encrypts the keys with a passphrase. The outputs are text strings that
12 | can be saved into files. Those files are called PEM (Privacy Enhanced Mail) files.
13 |
14 | ```rust,editable
15 | # extern crate openssl;
16 |
17 | use openssl::rsa::{Rsa, Padding};
18 | use openssl::symm::Cipher;
19 |
20 | fn main() {
21 | let passphrase = "rust_by_example";
22 |
23 | let rsa = Rsa::generate(1024).unwrap();
24 | let private_key: Vec = rsa.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), passphrase.as_bytes()).unwrap();
25 | let public_key: Vec = rsa.public_key_to_pem().unwrap();
26 |
27 | println!("Private key: {}", String::from_utf8(private_key).unwrap());
28 | println!("Public key: {}", String::from_utf8(public_key).unwrap());
29 | }
30 | ```
31 |
32 | Next, we can import public and private keys from the PEM document.
33 | In the example, we demonstrate how to encrypt a byte array of data using the
34 | public key. Such encrypted data can only be decrypted by the correspding private key.
35 |
36 |
37 | ```rust,editable
38 | # extern crate openssl;
39 |
40 | use openssl::rsa::{Rsa, Padding};
41 |
42 | fn main() {
43 | let passphrase = "rust_by_example";
44 |
45 | let public_key_pem = "-----BEGIN PUBLIC KEY-----
46 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC+Jx89MjzbWw9PPh0dffD+i2c
47 | J7XMioLndImQvQiNJjZ00zyxjgt4+wkual+ZHhH94HIjRIeLI+ncBEjFMa1xIzHT
48 | exz/pvJUCsHNxNK9958zR0E997xxSf3C2Lu8BWtJG348xd5QNzb+R+i963PtcAsQ
49 | fCu+q5gbqqtQEIjlMwIDAQAB
50 | -----END PUBLIC KEY-----";
51 |
52 | let private_key_pem = "-----BEGIN RSA PRIVATE KEY-----
53 | Proc-Type: 4,ENCRYPTED
54 | DEK-Info: AES-128-CBC,43371B6CECDB096AC2A362FD33BF4B07
55 |
56 | aIs3x9UBN95VJJFsd1ddYxmwAKQdFE5BJwZVYtidV+cZ4Qpmg9tdBLm8AhF5bVGR
57 | FzAVMxTEFQgwT4o2jH2UxRkRmChwNy6aqdGteDIK6yXQK7//GMmxhbvqMmFzwdof
58 | 2E7Jkq3BQQEqMFu2CxRUPUFYRIebEIZSDCD3PoJ6p7a77qwm/KCXCbad/DqtOGkJ
59 | wOkPH5AXLIu02MJfs+vcLswXFMlq7aaUrAv5WGt1SpKz9Co6bplSYDG7JE+906Uw
60 | MIg4XDJTJDKCKyDaPkMydw6StvyNuZfIYUNIofulLci7yoNEGvwQHsHCaHr6n4bt
61 | I4iC9CbkEcPbf06HAWGFfsexeLGf9mU0HVsZi83QdMhWMbOREakFU755AMvTeB8w
62 | IMCNn55nzJlSHooKuvJAmbqBBb4+wqgwnoYQEVZmTDZxqT/eR08Zl9d1QeKB+1fw
63 | gjZmY/10kFLnTKlWGIaLIu60ehbXxZeFbW+m1pF9uHEiIkWgkrHNjKfzWh5EyfhY
64 | vXxWuZH92ZP/nioGzVQr00oSEPLwW1RSoAx3jPuu1EILNu7lFL896CsDZpa1Oigf
65 | OMxk0GhMuKs4H6TlHmx5a0TOAcGYWEbnqXi+KUw7pMPFiEs1/2crFI6QfQx8R7dL
66 | /ohKFvksPExsB196RZ1PFyMdryOr/mCqI4nBT+KzPz4zJF2iTMGq3NFQI2MvW/4g
67 | WMwsyQtIJQviFJpYlQpOVBFaeB69oHJMxfauM8OdEU8yomFl3sAVagNxPfiWsGt4
68 | LRsReK2BDT/pnhhZG96qSsNPwQlrwffBleTy9BGSuHHox6A7GKyVAAOMND/TY1ak
69 | -----END RSA PRIVATE KEY-----";
70 |
71 | let data = "A quick brown fox jumps over the lazy dog.";
72 |
73 | // Encrypt with public key
74 | let rsa = Rsa::public_key_from_pem(public_key_pem.as_bytes()).unwrap();
75 | let mut buf: Vec = vec![0; rsa.size() as usize];
76 | let _ = rsa.public_encrypt(data.as_bytes(), &mut buf, Padding::PKCS1).unwrap();
77 | println!("Encrypted: {:?}", buf);
78 |
79 | let data = buf;
80 |
81 | // Decrypt with private key
82 | let rsa = Rsa::private_key_from_pem_passphrase(private_key_pem.as_bytes(), passphrase.as_bytes()).unwrap();
83 | let mut buf: Vec = vec![0; rsa.size() as usize];
84 | let _ = rsa.private_decrypt(&data, &mut buf, Padding::PKCS1).unwrap();
85 | println!("Decrypted: {}", String::from_utf8(buf).unwrap());
86 | }
87 | ```
88 |
89 |
--------------------------------------------------------------------------------
/src/rand.md:
--------------------------------------------------------------------------------
1 | # Random numbers
2 |
3 | A lot of applications require random numbers. The [`rand`](https://crates.io/crates/rand) crate is a very popular library in Rust to generate random numbers.
4 | It supports a wide variety of random number generators and distributions, each with a different performance and security trade off.
5 | For more details, you can read the [Rust Rand Book](https://rust-random.github.io/book/intro.html).
6 | To use the `rand` crate, just do the following in your `Cargo.toml` file.
7 |
8 | ```
9 | [dependencies]
10 | rand = "0.7.3"
11 | ```
12 |
13 | ## Get a random number
14 |
15 | To get a random number, you can simply do the following.
16 |
17 | ```rust,editable
18 | # extern crate rand;
19 |
20 | fn main() {
21 | let i: i32 = rand::random();
22 | println!("The random i32 is {}", i);
23 | }
24 | ```
25 |
26 | The `random()` is smart enough to know the primitive type it is **supposed** to generate. Check out the example below.
27 |
28 | ```rust,editable
29 | # extern crate rand;
30 |
31 | fn main() {
32 | let x: u8 = rand::random();
33 | println!("The random u8 is {}", x);
34 |
35 | let x: f64 = rand::random();
36 | println!("The random f64 is {}", x);
37 |
38 | let x:bool = rand::random();
39 | println!("The random bool {}", x);
40 | }
41 | ```
42 |
43 | What about generating a random number within a range? For that, you need to
44 | create a random number generator and call its `gen_range()` function.
45 |
46 | ```rust,editable
47 | # extern crate rand;
48 |
49 | use rand::thread_rng;
50 | use rand::Rng;
51 |
52 | fn main() {
53 | let mut rng = thread_rng();
54 | let y: f64 = rng.gen_range(-10.0, 10.0);
55 | println!("Number from -10. to 10.: {}", y);
56 | println!("Number from 0 to 9: {}", rng.gen_range(0, 10));
57 | }
58 | ```
59 |
60 | ## Get a series of random numbers
61 |
62 | In order to get a series of random numbers, you could call the `random()` function
63 | multiple times. But that is slow since every time it needs to instantiate and seed
64 | a new random number generator. It is faster to create the generator once and
65 | call its `gen()` function repeatedly.
66 |
67 | ```rust,editable
68 | # extern crate rand;
69 |
70 | use rand::thread_rng;
71 | use rand::Rng;
72 |
73 | fn main() {
74 | let mut rng = thread_rng();
75 | for i in 1..10 {
76 | println!("Random number #{}: {}", i, rng.gen_range(0, 100));
77 | }
78 | }
79 | ```
80 |
81 | The generator can quickly fill an array with random integers.
82 |
83 | ```rust,editable
84 | # extern crate rand;
85 |
86 | use rand::thread_rng;
87 | use rand::Rng;
88 |
89 | fn main() {
90 | let mut arr = [0i32; 9];
91 | thread_rng().try_fill(&mut arr[..]);
92 | println!("Random number array {:?}", arr);
93 | }
94 | ```
95 |
96 | Another neat feature of the generator is that it can generate random numbers
97 | from a probability distribution.
98 |
99 | ```rust,editable
100 | # extern crate rand;
101 |
102 | use rand::thread_rng;
103 | use rand::Rng;
104 |
105 | fn main() {
106 | let mut rng = thread_rng();
107 | let distr = rand::distributions::Uniform::new_inclusive(1, 100);
108 | let mut nums = [0i32; 3];
109 | for x in &mut nums {
110 | *x = rng.sample(distr);
111 | }
112 | println!("Some numbers: {:?}", nums);
113 | }
114 | ```
115 |
116 | ## WebAssembly
117 |
118 | Under the hood, the `rand` crate uses the operating system's native random
119 | functions to seed the random number generator. This initial entropy seed
120 | comes from the hardware noise. However, when Rust programs inside virtual
121 | machines like WebAssembly, it does not have access to native random hardware.
122 |
123 | When the `rand` crate is compiled for WebAssembly, it has a special feature
124 | to use JavaScript's random function to seed the generator. This works when
125 | your WebAssembly application is running inside a JavaScript host, such as in
126 | a web browser or in Node.js. To enable that, do the following in your `Cargo.toml`.
127 |
128 | ```
129 | [dependencies]
130 | rand = { version = "0.7.3", features = ["wasm-bindgen"] }
131 | ```
132 |
133 | Then you must use one of the [`wasm-bindgen`](https://rustwasm.github.io/docs/wasm-bindgen/) compatible tools to instrument
134 | your Rust code to call external JavaScript and a JavaScript shim to be called
135 | from Rust.
136 |
137 | - The [`ssvmup`](https://www.npmjs.com/package/ssvmup) and [`ssvm`](https://www.npmjs.com/package/ssvm) tools enable high performance Rust + JavaScript hybrid applications in Node.js. [Check out this tutorial](https://cloud.secondstate.io/server-side-webassembly/getting-started).
138 | - The [`wasm-pack`](https://rustwasm.github.io/wasm-pack/) tool can generate Rust and JavaScript code for the V8 engine.
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/src/rbeext.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/second-state/rust-by-example-ext/b3821ad2ff05b07b6b79c9d81c4819f327e58d83/src/rbeext.gif
--------------------------------------------------------------------------------
/src/regex.md:
--------------------------------------------------------------------------------
1 | # Regular expression
2 |
3 | Coming soon!
4 |
--------------------------------------------------------------------------------
/src/serde.md:
--------------------------------------------------------------------------------
1 | # Serialization and deserialization
2 |
3 | Serialization is a core language feature in the era of web applications. When one program needs to talk to another program
4 | over the Internet, it needs to serialize its data into a format that can be transmitted through the network. The receiving
5 | program uses deserialization to reconstruct the data.
6 |
7 | In Rust, most applications use the [`serde`](https://crates.io/crates/serde) crate to manage serialization and deserialization. In this chapter, we will cover how to serialize typed Rust data into [JSON strings](serde/json.md)
8 | or [byte arrays](serde/bincode.md). We will also discuss how to serialize third party structs in libraries.
9 |
10 | Checkout the [official documentation](https://serde.rs/) for the `serde` crate.
11 |
--------------------------------------------------------------------------------
/src/serde/bincode.md:
--------------------------------------------------------------------------------
1 | # Serialize into binary
2 |
3 | JSON strings are portable across almost all programming languages and frameworks. But for communication between Rust programs, a binary format could be much more efficient. Here is where `bincode` comes into play. To use the `bincode` crate, you just need to add the following dependencies to your `Cargo.toml` file.
4 |
5 | ```
6 | [dependencies]
7 | serde = { version = "1.0", features = ["derive"] }
8 | bincode = "1.2.1"
9 | ```
10 |
11 | > **Note:** Since the `bincode` crate is not included the Rust Playground by default, the code examples in this article are not interactive. We will show the program output in the text. You can run these examples from [this cargo project](https://github.com/second-state/rust-by-example-ext/tree/master/examples/serde/bincode).
12 |
13 | The example below shows how to serialize a simple Rust primitive data type `i32` into a byte array, and then deserialize it back. You can pass this byte array to other Rust programs over the Internet.
14 |
15 | ```rust,noplaypen
16 | use bincode;
17 |
18 | fn main() {
19 | let x: i32 = 5;
20 | let xs: Vec = bincode::serialize(&x).unwrap();
21 | println!("i32 number {} serializes into byte array {:?}", x, xs);
22 | let xd: i32 = bincode::deserialize(&xs).unwrap();
23 | assert_eq!(x, xd);
24 | }
25 | ```
26 |
27 | Result
28 |
29 | ```
30 | i32 number 5 serializes into byte array [5, 0, 0, 0]
31 | ```
32 |
33 | Here are more examples showing the serialization and deserialization of Rust primitive data types. Edit the code below to try more Rust types, and run it to see the results.
34 |
35 | ```rust,noplaypen
36 | use bincode;
37 |
38 | fn main() {
39 | let x: i32 = 5;
40 | let xs: Vec = bincode::serialize(&x).unwrap();
41 | println!("i32 number {} serializes into byte array {:?}", x, xs);
42 | let xd: i32 = bincode::deserialize(&xs).unwrap();
43 | assert_eq!(x, xd);
44 |
45 | let x: f32 = 3.14;
46 | let xs = bincode::serialize(&x).unwrap();
47 | println!("f32 number {} serializes into byte array {:?}", x, xs);
48 |
49 | let x: Vec = vec![1, 2, 3];
50 | let xs = bincode::serialize(&x).unwrap();
51 | println!("Vec {:?} serializes into byte array {:?}", x, xs);
52 | let xd: Vec = bincode::deserialize(&xs).unwrap();
53 | assert_eq!(x, xd);
54 |
55 | let x: Vec = vec![3.141, 2.718, 1.618];
56 | let xs = bincode::serialize(&x).unwrap();
57 | println!("Vec {:?} serializes into byte array {:?}", x, xs);
58 | let xd: Vec = bincode::deserialize(&xs).unwrap();
59 | assert_eq!(x, xd);
60 |
61 | let x: (i32, &str, f32, bool) = (1, "hello", 4.5, true);
62 | let xs = bincode::serialize(&x).unwrap();
63 | println!("tuple {:?} serializes into byte array {:?}", x, xs);
64 | let xd: (i32, &str, f32, bool) = bincode::deserialize(&xs).unwrap();
65 | assert_eq!(x, xd);
66 |
67 | let x = ((1u8, 2u16), (3.141f32, 'a'), true);
68 | let xs = bincode::serialize(&x).unwrap();
69 | println!("nested tuple {:?} serializes into byte array {:?}", x, xs);
70 | }
71 | ```
72 |
73 | Result
74 |
75 | ```
76 | i32 number 5 serializes into byte array [5, 0, 0, 0]
77 | f32 number 3.14 serializes into byte array [195, 245, 72, 64]
78 | Vec [1, 2, 3] serializes into byte array [3, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3]
79 | Vec [3.141, 2.718, 1.618] serializes into byte array [3, 0, 0, 0, 0, 0, 0, 0, 37, 6, 73, 64, 182, 243, 45, 64, 160, 26, 207, 63]
80 | tuple (1, "hello", 4.5, true) serializes into byte array [1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 0, 0, 144, 64, 1]
81 | nested tuple ((1, 2), (3.141, 'a'), true) serializes into byte array [1, 2, 0, 37, 6, 73, 64, 97, 1]
82 | ```
83 |
84 | What about structs and other custom Rust data types? Well, you just need to annotate them with `serde` and they will automagically get serialization capabilities!
85 |
86 | ```rust,noplaypen
87 | use serde::{Serialize, Deserialize};
88 | use bincode;
89 |
90 | #[derive(Serialize, Deserialize, Debug)]
91 | struct Point {
92 | x: f32,
93 | y: f32
94 | }
95 |
96 | #[derive(Serialize, Deserialize, Debug)]
97 | struct Line {
98 | points: Vec,
99 | valid: bool,
100 | length: f32,
101 | desc: String
102 | }
103 |
104 | fn main() {
105 | let point1: Point = Point {x:1.0, y:2.0};
106 | let point2: Point = Point {x:3.0, y:4.0};
107 | let point1s = bincode::serialize(&point1).unwrap();
108 | let point2s = bincode::serialize(&point2).unwrap();
109 | println!("struct Point serializes into byte array {:?}", point1s);
110 | println!("struct Point serializes into byte array {:?}", point2s);
111 |
112 | let length = ((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)).sqrt();
113 | let valid = if length == 0.0 { false } else { true };
114 | let line = Line { points: vec![point1, point2], valid: valid, length: length, desc: "a thin line".to_string() };
115 | let lines = bincode::serialize(&line).unwrap();
116 | println!("struct Line serializes into byte array {:?}", lines);
117 |
118 | let lined: Line = bincode::deserialize(&lines).unwrap();
119 | assert_eq!(lined.desc, "a thin line");
120 | assert_eq!(lined.points[1].x, 3.0);
121 | }
122 | ```
123 |
124 | Result
125 |
126 | ```
127 | struct Point serializes into byte array [0, 0, 128, 63, 0, 0, 0, 64]
128 | struct Point serializes into byte array [0, 0, 64, 64, 0, 0, 128, 64]
129 | struct Line serializes into byte array [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 128, 64, 1, 243, 4, 53, 64, 11, 0, 0, 0, 0, 0, 0, 0, 97, 32, 116, 104, 105, 110, 32, 108, 105, 110, 101]
130 | ```
131 |
--------------------------------------------------------------------------------
/src/serde/feature.md:
--------------------------------------------------------------------------------
1 | # Serialize third party library types
2 |
3 | Many third party Rust crates already support serialization and deserialization
4 | via the `serde` crate. You just need to enable the `serde` feature when you
5 | declare the crate as dependency.
6 |
7 | ```
8 | [dependencies]
9 | serde = { version = "1.0", features = ["derive"] }
10 | serde_json = "1.0"
11 | num-bigint = { version = "0.2", features = ["serde"] }
12 | num = "0.2"
13 | ```
14 |
15 | > **Note:** Since the `serde` feature is not enabled on third party crates in the Rust Playground by default, the code examples in this article are not interactive. We will show the program output in the text. You can run these examples from [this cargo project](https://github.com/second-state/rust-by-example-ext/tree/master/examples/serde/feature).
16 |
17 | The example below shows how to serialize and deserialize a `BigInt` struct type from the `num_bigint` crate. It just works out of the box.
18 |
19 | ```rust,noplaypen
20 | use num::bigint::ToBigInt;
21 | use num_bigint::BigInt;
22 |
23 | fn main() {
24 | let a = 3.to_bigint().unwrap();
25 | let x = num::pow(a, 247);
26 | let xs = serde_json::to_string(&x).unwrap();
27 | let xd: BigInt = serde_json::from_str(&xs).unwrap();
28 | assert_eq!(x, xd);
29 |
30 | println!("3**247 is {} and serializes to {}", x, xs);
31 | }
32 | ```
33 |
34 | Result
35 |
36 | ```
37 | 3**247 is 7062361041362837614435796717454722507454089864783271756927542774477268334591598635421519542453366332460075473278915787 and serializes to [1,[3323516107,3672165520,4080039719,3245710364,216105283,4292129601,4006727268,340573034,2851604588,3366124224,3797961372,1024846073,179]]
38 | ```
39 |
40 | What about third party crates that are not yet to support the `serde` feature?
41 | The `serde` crate provides another approach called [remote derive](https://serde.rs/remote-derive.html). Basically, you create a local copy of the remote type
42 | and then serialize the local type through a bridge. Check out the [documentation](https://serde.rs/remote-derive.html).
43 |
44 |
--------------------------------------------------------------------------------
/src/serde/json.md:
--------------------------------------------------------------------------------
1 | # Serialize into JSON strings
2 |
3 | To use the `serde` crate, you just need to add the following dependencies to your `Cargo.toml` file.
4 |
5 | ```
6 | [dependencies]
7 | serde = { version = "1.0", features = ["derive"] }
8 | serde_json = "1.0"
9 | ```
10 |
11 | The example below shows how to serialize a simple Rust primitive data type `i32` into a JSON string, and then deserialize it back. Run it! You can pass this JSON string to other Rust programs or Internet applications written in other languages.
12 |
13 | ```rust,editable
14 | # extern crate serde;
15 |
16 | fn main() {
17 | let x: i32 = 5;
18 | let xs = serde_json::to_string(&x).unwrap();
19 | println!("i32 number {} serializes into string {}", x, xs);
20 | let xd: i32 = serde_json::from_str(&xs).unwrap();
21 | assert_eq!(x, xd);
22 | }
23 | ```
24 |
25 | Here are more examples showing the serialization and deserialization of Rust primitive data types. Rust arrays and tuples are all serialized to JSON arrays. Edit the code below to try more Rust types, and run it to see the results.
26 |
27 | ```rust,editable
28 | # extern crate serde;
29 |
30 | fn main() {
31 | let x: i32 = 5;
32 | let xs = serde_json::to_string(&x).unwrap();
33 | println!("i32 number {} serializes into string {}", x, xs);
34 | let xd: i32 = serde_json::from_str(&xs).unwrap();
35 | assert_eq!(x, xd);
36 |
37 | let x: f32 = 3.14;
38 | let xs = serde_json::to_string(&x).unwrap();
39 | println!("f32 number {} serializes into string {}", x, xs);
40 |
41 | let x: Vec = vec![1, 2, 3];
42 | let xs = serde_json::to_string(&x).unwrap();
43 | println!("Vec {:?} serializes into string {}", x, xs);
44 | let xd: Vec = serde_json::from_str(&xs).unwrap();
45 | assert_eq!(x, xd);
46 |
47 | let x: Vec = vec![3.141, 2.718, 1.618];
48 | let xs = serde_json::to_string(&x).unwrap();
49 | println!("Vec {:?} serializes into string {}", x, xs);
50 |
51 | let x: (i32, &str, f32, bool) = (1, "hello", 4.5, true);
52 | let xs = serde_json::to_string(&x).unwrap();
53 | println!("tuple {:?} serializes into string {}", x, xs);
54 | let xd: (i32, &str, f32, bool) = serde_json::from_str(&xs).unwrap();
55 | assert_eq!(x, xd);
56 |
57 | let x = ((1u8, 2u16), (3.141f32, 'a'), true);
58 | let xs = serde_json::to_string(&x).unwrap();
59 | println!("nested tuple {:?} serializes into string {}", x, xs);
60 | }
61 | ```
62 |
63 | What about structs and other custom Rust data types? Well, you just need to annotate them and they will automagically get serialization capabilities! Run the example below and you can see the JSON string representation of these Rust structs.
64 |
65 | ```rust,editable
66 | # extern crate serde;
67 |
68 | use serde::{Serialize, Deserialize};
69 |
70 | #[derive(Serialize, Deserialize, Debug)]
71 | struct Point {
72 | x: f32,
73 | y: f32
74 | }
75 |
76 | #[derive(Serialize, Deserialize, Debug)]
77 | struct Line {
78 | points: Vec,
79 | valid: bool,
80 | length: f32,
81 | desc: String
82 | }
83 |
84 | fn main() {
85 | let point1: Point = Point {x:1.0, y:2.0};
86 | let point2: Point = Point {x:3.0, y:4.0};
87 | let point1s = serde_json::to_string(&point1).unwrap();
88 | let point2s = serde_json::to_string(&point2).unwrap();
89 | println!("struct Point serializes into string {}", point1s);
90 | println!("struct Point serializes into string {}", point2s);
91 |
92 | let length = ((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)).sqrt();
93 | let valid = if length == 0.0 { false } else { true };
94 | let line = Line { points: vec![point1, point2], valid: valid, length: length, desc: "a thin line".to_string() };
95 | let lines = serde_json::to_string(&line).unwrap();
96 | println!("struct Line serializes into string {}", lines);
97 |
98 | let lined: Line = serde_json::from_str(&lines).unwrap();
99 | assert_eq!(lined.desc, "a thin line");
100 | assert_eq!(lined.points[1].x, 3.0);
101 | }
102 | ```
103 |
104 |
--------------------------------------------------------------------------------
/src/webassembly.md:
--------------------------------------------------------------------------------
1 | # WebAssembly
2 |
3 | Besides operating system specific native binaries, Rust programs can also be compiled into managed
4 | code that runs inside containers or virtual machines. The reason to use managed code is runtime safety. That allows un-trusted
5 | Rust programs to run in environments such as web browsers and servers. Compared with native binaries, managed
6 | containers are also much easier to provision, limit access to resources, start, and stop on demand. A popular managed code compiler target is the WebAssembly virtual machine. With WebAssembly, your Rust programs can run side-by-side with Javascript in web browsers and servers.
7 |
8 | In this chapter, we will first show you how to [compile](webassembly/compile.md) and [run]((webassembly/run.md)) a very simple Rust function in WebAssembly. The simple example is followed by a [hello world example](webassembly/browser.md) running in web browsers.
9 |
10 | More complex Rust / WebAssembly examples come from the server-side. We will demonstrate how to [call Rust functions from Node.js](webassembly/nodejs.md) Javascript applications, as well as how to [access Node.js modules from Rust functions](webassembly/nodejshelper.md).
11 |
--------------------------------------------------------------------------------
/src/webassembly/browser.md:
--------------------------------------------------------------------------------
1 | # Rust Javascript binding through WebAssembly
2 |
3 | In the previous articles, we showed how to compile a Rust function into
4 | WebAssembly, and then call this function from Javascript in a web browser.
5 | However, that approach has some severe limitations. Specifically, WebAssembly
6 | has very limited support for data types. When a WebAssembly function is
7 | exported to Javascript, the input parameters and return values are limited
8 | 32 bit integers. You cannot even pass or return string values to the WebAssembly
9 | function! You could even do a Hello World!
10 |
11 | Fortunately, the `wasm-bindgen` project provides binding between Rust and
12 | Javascript. An easy way to `wasm-bindgen` in a browser project is to the
13 | `wasm-pack` tool. Here is how to install `wasm-pack` through the npm package manager.
14 |
15 | ```
16 | $ npm install -g wasm-pack
17 | ```
18 |
19 | In your project's `Cargo.toml`, add dependency for the `wasm-bindgen` crate.
20 |
21 | ```
22 | [dependencies]
23 | wasm-bindgen = "0.2.50"
24 | ```
25 |
26 | Below is a Rust library function we wrote. As you can see it now takes and returns `String` values. The function must be annotated with `#[wasm_bindgen]` in order for the Rust compiler toolchain to call `wasm-bindgen` to generate the necessary shim code that binds Rust and Javascript through WebAssembly.
27 |
28 | ```
29 | use wasm_bindgen::prelude::*;
30 |
31 | #[wasm_bindgen]
32 | pub fn say(s: String) -> String {
33 | let r = String::from("hello ");
34 | return r + &s;
35 | }
36 | ```
37 |
38 | Build the `wasm` bytecode application and its Javascript helper files via `wasm-pack`.
39 | The generated files are in the `pkg` directory.
40 |
41 | ```
42 | $ wasm-pack build --target web
43 | ```
44 |
45 | From a web browser's Javascript console, you can load the generated Javascript file, export the Rust function, and call it.
46 |
47 | ```
48 | import init, { say } from 'pkg/hello_lib.js';
49 | init();
50 | say("Michael");
51 | ```
52 |
53 | The result is `Hello Michael`.
54 |
55 | To see the complete source code and run it on a web page, [checkout here](https://github.com/second-state/wasm-learning/tree/master/browser/hello).
56 |
57 |
--------------------------------------------------------------------------------
/src/webassembly/compile.md:
--------------------------------------------------------------------------------
1 | # Compile targets for WebAssembly
2 |
3 | There are several Rust compiler targets for WebAssembly. They differ
4 | because different WebAssembly VMs supports different extensions.
5 | For example, some WebAssembly VMs have access the WASI extension that allows
6 | access to system resources such as the file system.
7 | The `wasm32` target is the most general target that produces plain WebAssembly
8 | bytecode that can run on any WebAssembly VM.
9 |
10 | To add the `wasm32` target to the compiler toolchain, do the following on your operating system's command line.
11 |
12 | ```
13 | $ rustup target add wasm32-unknown-unknown
14 | $ rustup override set nightly
15 | $ rustup target add wasm32-unknown-unknown --toolchain nightly
16 | ```
17 |
18 | > Check out the [complete example source code here](https://github.com/second-state/wasm-learning/tree/master/browser/triple).
19 |
20 | Now, create a Rust library project in cargo, and create a public function like the following.
21 |
22 | ```
23 | #[no_mangle]
24 | pub extern fn triple(x: i32) -> i32 {
25 | return 3 * x;
26 | }
27 | ```
28 |
29 | You can build the project into WebAssembly bytecode using `cargo`.
30 |
31 | ```
32 | $ cargo +nightly build --target wasm32-unknown-unknown --release
33 | ```
34 |
35 | The compiled WebAssembly bytecode application is a single file with the `wasm` extension. For example, here is the `wasm` file from our example `target/wasm32-unknown-unknown/release/my_project_name.wasm`.
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/webassembly/nodejs.md:
--------------------------------------------------------------------------------
1 | # Rust and WebAssembly on the server side
2 |
3 | In the previous article, we discussed how to run Rust functions in web browsers.
4 | In this article, we will show you how to do this on the server side.
5 | We believe that Rust and WebAssembly [brings a lot of benefits](https://cloud.secondstate.io/server-side-webassembly/why) to server side
6 | applications.
7 |
8 | On the server side, we use the open source [Second State VM (SSVM)](https://github.com/second-state/SSVM) to execute
9 | Rust and WebAssembly functions in the Node.js environment. We still use
10 | the `wasm-bindgen` crate to support binding between Rust and
11 | Javascript. An easy way to `wasm-bindgen` in a SSVM Node.js project is to the
12 | `ssvmup` tool. Here is how to install `ssvmup` through the npm package manager.
13 |
14 | ```
15 | $ npm install -g ssvmup
16 | ```
17 |
18 | In your project's `Cargo.toml`, add dependency for the `wasm-bindgen` crate.
19 |
20 | ```
21 | [dependencies]
22 | wasm-bindgen = "=0.2.61"
23 | ```
24 |
25 | ## Hello world
26 |
27 | Below is a Rust hello world function we wrote. The function must be annotated with `#[wasm_bindgen]` in order for the Rust compiler toolchain to call `wasm-bindgen` to generate the necessary shim code that binds Rust and Javascript through WebAssembly.
28 |
29 | ```
30 | use wasm_bindgen::prelude::*;
31 |
32 | #[wasm_bindgen]
33 | pub fn say(s: String) -> String {
34 | let r = String::from("hello ");
35 | return r + &s;
36 | }
37 | ```
38 |
39 | Build the `wasm` bytecode application and its Javascript helper files via `ssvmup`.
40 | The generated files are in the `pkg` directory.
41 |
42 | ```
43 | $ ssvmup build
44 | ```
45 |
46 | In a Javascript application, you can load the generated Javascript file, export the Rust function, and call it.
47 |
48 | ```
49 | const { say } = require('pkg/hello_lib.js');
50 | say("Michael");
51 | ```
52 |
53 | You can now run the Javascript application from Node.js command line.
54 |
55 | ```
56 | $ node app.js
57 | Hello Michael
58 | ```
59 |
60 | > To see the complete source code and run it in a Node.js server, [checkout here](https://github.com/second-state/wasm-learning/tree/master/nodejs/hello).
61 |
62 | ## Beyond simple arguments
63 |
64 | Using the `serde` crate, we can pass in and return arbitary Javascript values to/from Rust functions. The idea is to serialize the entire set of call arguments, and return values, into JSON strings. First, add `serde` to your dependencies.
65 |
66 | ```
67 | [dependencies]
68 | serde = { version = "1.0", features = ["derive"] }
69 | serde_json = "1.0"
70 | wasm-bindgen = "=0.2.61"
71 | ```
72 |
73 | The Rust function takes two floating point numbers, and returns the product of
74 | the two. Notice that the input and return values are all encoded in JSON.
75 |
76 | ```
77 | use wasm_bindgen::prelude::*;
78 |
79 | #[wasm_bindgen]
80 | pub fn area(sides: &str) -> String {
81 | let s: (f32, f32) = serde_json::from_str(&sides).unwrap();
82 | let a = s.0 * s.1;
83 | return serde_json::to_string(&a).unwrap();
84 | }
85 | ```
86 |
87 | Build and create the `pkg` folder for the compiled `wasm` bytecode file and the Javascript shim file `my_project_name.js`.
88 |
89 | ```
90 | $ ssvmup build
91 | ```
92 |
93 | The Javascript calling program in Node.js looks like the following.
94 |
95 | ```
96 | const { area } = require('pkg/my_project_name.js');
97 | var x = [10., 5.];
98 | console.log( area(JSON.stringify(x)) );
99 | ```
100 |
101 | Run the Node.js app shows the following result.
102 |
103 | ```
104 | $ node app.js
105 | 50.0
106 | ```
107 |
108 | > To see the complete source code and run it in Node.js, [checkout here](https://github.com/second-state/wasm-learning/tree/master/nodejs/json_io).
109 |
110 | ## Structs and objects
111 |
112 | Using the `serde` crate, we can pass in complex Javascript objects and arrays to Rust functions, and return Javascript objects. Javascript objects are mapped to Rust structs, and arrays are mapped to Rust tuples.
113 |
114 | The Rust function `draw()` takes two JSON strings, each representing a `Point` struct, and returns a JSON string representing a `Line` struct.
115 |
116 | ```
117 | use wasm_bindgen::prelude::*;
118 | use serde::{Serialize, Deserialize};
119 |
120 | #[derive(Serialize, Deserialize, Debug)]
121 | struct Point {
122 | x: f32,
123 | y: f32
124 | }
125 |
126 | #[derive(Serialize, Deserialize, Debug)]
127 | struct Line {
128 | points: Vec,
129 | valid: bool,
130 | length: f32,
131 | desc: String
132 | }
133 |
134 | #[wasm_bindgen]
135 | pub fn draw(points: &str) -> String {
136 | let ps: (Point, Point, String) = serde_json::from_str(&points).unwrap();
137 | let length = ((ps.0.x - ps.1.x) * (ps.0.x - ps.1.x) + (ps.0.y - ps.1.y) * (ps.0.y - ps.1.y)).sqrt();
138 |
139 | let valid = if length == 0.0 { false } else { true };
140 | let line = Line { points: vec![ps.0, ps.1], valid: valid, length: length, desc: ps.2 };
141 | return serde_json::to_string(&line).unwrap();
142 | }
143 | ```
144 |
145 | Build and create the `pkg` folder for the compiled `wasm` bytecode file and the Javascript shim file `my_project_name.js`.
146 |
147 | ```
148 | $ ssvmup build
149 | ```
150 |
151 | The Javascript calling program in Node.js looks like the following.
152 |
153 | ```
154 | const { draw } = require('pkg/my_project_name.js');
155 | var x = [{x:1.5, y:3.8}, {x:2.5, y:5.8}, "A thin red line"];
156 | console.log( draw(JSON.stringify(x)) );
157 | ```
158 |
159 | Run the Node.js app shows the following result.
160 |
161 | ```
162 | $ node app.js
163 | {"points":[{"x":1.5,"y":3.8},{"x":2.5,"y":5.8}],"valid":true,"length":2.2360682,"desc":"A thin red line"}
164 | ```
165 |
166 | > To see the complete source code and run it in Node.js, [checkout here](https://github.com/second-state/wasm-learning/tree/master/nodejs/json_io).
167 |
168 | ## Binary data
169 |
170 | A server side function often needs to process binary data directly. The SSVM
171 | toolchain supports that use case out of the box.
172 | In this example, we will show you how to create a Rust function on the server
173 | to compute a SHA3 cryptographic digest for an arbitary input binary data array. First, add
174 | the `sh3` crate as a dependency.
175 |
176 | ```
177 | [dependencies]
178 | sha3 = "0.8.2"
179 | wasm-bindgen = "=0.2.61"
180 | ```
181 |
182 | Below is the Rust function that computes the SHA3 digest value. Notice that both
183 | its input and return values are byte arrays.
184 |
185 | ```
186 | use wasm_bindgen::prelude::*;
187 | use sha3::{Digest, Sha3_256};
188 |
189 | #[wasm_bindgen]
190 | pub fn sha3_digest(v: &[u8]) -> Vec {
191 | return Sha3_256::digest(&v).as_slice().to_vec();
192 | }
193 | ```
194 |
195 | Build and create the `pkg` folder for the compiled `wasm` bytecode file and the Javascript shim file `my_project_name.js`.
196 |
197 | ```
198 | $ ssvmup build
199 | ```
200 |
201 | The Javascript calling program in Node.js looks like the following.
202 |
203 | ```
204 | const { sha3_digest } = require('pkg/my_project_name.js');
205 | console.log( sha3_digest(encoder.encode("This is an important message")) );
206 | ```
207 |
208 | Run the Node.js app shows the following result.
209 |
210 | ```
211 | $ node app.js
212 | 000000 57 1b e7 d1 bd 69 fb 31 9f 0a d3 fa 0f 9f 9a b5 W.çѽiû1..Óú...µ
213 | 000010 2b da 1a 8d 38 c7 19 2d 3c 0a 14 a3 36 d3 c3 cb +Ú..8Ç.-<..£6ÓÃË
214 | ```
215 |
216 | > To see the complete source code and run it in Node.js, [checkout here](https://github.com/second-state/wasm-learning/tree/master/nodejs/functions).
217 |
--------------------------------------------------------------------------------
/src/webassembly/nodejshelper.md:
--------------------------------------------------------------------------------
1 | # Call Javascript from Rust
2 |
3 | In the previous article, we discussed how to call Rust functions from
4 | Javascript. How about the other way around? The `nodejs-helper` crate
5 | enables Rust functions to call Javascript functions in Node.js.
6 |
7 | > To see the complete source code and run it in Node.js, [checkout here](https://github.com/second-state/wasm-learning/tree/master/nodejs/nodejs_example).
8 |
9 | In your project's `Cargo.toml`, add dependency for the [`nodejs-helper`](https://github.com/second-state/nodejs-helper) and `wasm-bindgen` crates.
10 |
11 | ```
12 | [dependencies]
13 | wasm-bindgen = "=0.2.61"
14 | nodejs-helper = "0.0.3"
15 | ```
16 |
17 | ## Console and time
18 |
19 | Recall that WebAssembly is a very simple and standalone virtual machine. It
20 | has no access the operating system's standard input / output, as well as
21 | features such as the system clock. In our Rust function, we can
22 | rely on the Javascript functions in Node.js to access those festures.
23 |
24 | ```
25 | use wasm_bindgen::prelude::*;
26 |
27 | #[wasm_bindgen]
28 | pub fn show_now() {
29 | nodejs_helper::console::log("Timestamp now: ");
30 | nodejs_helper::console::log(&nodejs_helper::date::timestamp());
31 | }
32 |
33 | #[wasm_bindgen]
34 | pub fn utc_now() {
35 | nodejs_helper::console::log("UTC time: ");
36 | nodejs_helper::console::log(&nodejs_helper::date::utc_string());
37 | }
38 |
39 | #[wasm_bindgen]
40 | pub fn my_time(tz: &str) {
41 | nodejs_helper::console::log(tz);
42 | nodejs_helper::console::log(&nodejs_helper::date::format_date("en-US", "long", "numeric", "long", "numeric", tz, "short"));
43 | }
44 | ```
45 |
46 | Build the `wasm` bytecode application and its Javascript helper files via `ssvmup`.
47 | The generated files are in the `pkg` directory.
48 |
49 | ```
50 | $ ssvmup build
51 | ```
52 |
53 | In a Javascript application, you can load the generated Javascript file, export the Rust function, and call it.
54 |
55 | ```
56 | const { show_now, utc_now, my_time } = require('pkg/nodejs_example.js');
57 |
58 | show_now();
59 | utc_now();
60 | my_time("America/Chicago");
61 | ```
62 |
63 | You can now run the Javascript application from Node.js command line.
64 |
65 | ```
66 | $ node date.js
67 | Timestamp now:
68 | 1588013800826
69 | UTC time:
70 | Mon, 27 Apr 2020 18:56:40 GMT
71 | America/Chicago
72 | Monday, April 27, 2020, CDT
73 | ```
74 |
75 | ## File system access
76 |
77 | The Rust functions in this section read an image file from the local file system, resize it, and write back to the file system. It also uses the Javascript console tool to measure the time spent on each task.
78 |
79 | ```
80 | [dependencies]
81 | serde = { version = "1.0", features = ["derive"] }
82 | serde_json = "1.0"
83 | wasm-bindgen = "=0.2.61"
84 | image = "0.23.0"
85 | ```
86 |
87 | The Rust function takes
88 | a JSON string input that contains the image file names and resized dimensions.
89 |
90 | ```
91 | use wasm_bindgen::prelude::*;
92 |
93 | #[derive(Serialize, Deserialize)]
94 | #[derive(Copy, Clone, Debug)]
95 | pub struct Dimension {
96 | pub width: u32,
97 | pub height: u32,
98 | }
99 |
100 | #[derive(Serialize, Deserialize)]
101 | pub struct Picture {
102 | pub dim: Dimension,
103 | pub raw: Vec,
104 | }
105 |
106 | #[wasm_bindgen]
107 | pub fn resize_file(input: &str) {
108 | // Use JSON to pass multiple call arguments
109 | let p: (Dimension, String, String) = serde_json::from_str(input).unwrap();
110 |
111 | nodejs_helper::console::time("Resize file");
112 | let raw = nodejs_helper::fs::read_file_sync(&p.1);
113 | nodejs_helper::console::time_log("Resize file", "Done reading");
114 | let src = Picture {
115 | dim: p.0,
116 | raw: raw,
117 | };
118 | let target = resize_impl(&src);
119 | nodejs_helper::console::time_log("Resize file", "Done resizing");
120 |
121 | nodejs_helper::fs::write_file_sync(&p.2, &target.raw);
122 | nodejs_helper::console::time_log("Resize file", "Done writing");
123 | nodejs_helper::console::time_end("Resize file");
124 | }
125 |
126 | pub fn resize_impl(src: &Picture) -> Picture {
127 | // ... use the img crate to resize ...
128 | }
129 | ```
130 |
131 | Build and create the `pkg` folder for the compiled `wasm` bytecode file and the Javascript shim file `my_project_name.js`.
132 |
133 | ```
134 | $ ssvmup build
135 | ```
136 |
137 | The Javascript calling program in Node.js looks like the following.
138 |
139 | ```
140 | const { resize_file } = require('../pkg/nodejs_example.js');
141 |
142 | const dim = {
143 | width: 100,
144 | height: 100
145 | };
146 |
147 | resize_file(JSON.stringify([dim, 'cat.png', `test.png`]));
148 | ```
149 |
150 | Run the Node.js app shows the following result.
151 |
152 | ```
153 | $ node image.js
154 | Resize file: 5.603ms Done reading
155 | Resize file: 1506.694ms Done resizing
156 | Resize file: 1507.634ms Done writing
157 | Resize file: 1507.977ms
158 | ```
159 |
160 | ## Sqlite database access
161 |
162 | First, you must have the `better-sqlite3` module installed in
163 | your Node.js setup. The Rust function will access a sqlite database through this module.
164 |
165 | ```
166 | $ npm i better-sqlite3
167 | ```
168 |
169 | The Rust functions to create, update, and query a Sqlite database on the local
170 | file system are as follows.
171 |
172 |
173 | ```
174 | use wasm_bindgen::prelude::*;
175 | use serde::{Serialize, Deserialize};
176 |
177 | #[derive(Serialize, Deserialize)]
178 | pub struct User {
179 | pub id: u32,
180 | pub full_name: String,
181 | pub created: String,
182 | }
183 |
184 | #[wasm_bindgen]
185 | pub fn create_sqlite(path: &str) {
186 | let sql_create = "
187 | CREATE TABLE users (
188 | id INTEGER PRIMARY KEY NOT NULL,
189 | full_name TEXT NOT NULL,
190 | created DATE NOT NULL
191 | );";
192 | let sql_insert = "
193 | INSERT INTO users
194 | VALUES
195 | (1, 'Bob McFett', '32-01-01'),
196 | (2, 'Angus Vader', '02-03-04'),
197 | (3, 'Imperator Colin', '01-01-01');";
198 |
199 | nodejs_helper::sqlite3::create(path);
200 | nodejs_helper::sqlite3::update(path, sql_create);
201 | nodejs_helper::sqlite3::update(path, sql_insert);
202 | }
203 |
204 | #[wasm_bindgen]
205 | pub fn query_sqlite(path: &str) {
206 | let sql_query = "SELECT * FROM users;";
207 | let rows: String = nodejs_helper::sqlite3::query(path, sql_query);
208 | let users: Vec = serde_json::from_str(&rows).unwrap();
209 | for user in users.into_iter() {
210 | nodejs_helper::console::log(&(user.id.to_string() + " : " + &user.full_name));
211 | }
212 | }
213 | ```
214 |
215 | Build and create the `pkg` folder for the compiled `wasm` bytecode file and the Javascript shim file `my_project_name.js`.
216 |
217 | ```
218 | $ ssvmup build
219 | ```
220 |
221 | The Javascript calling program in Node.js looks like the following.
222 |
223 | ```
224 | const { create_sqlite, query_sqlite } = require('pkg/nodejs_example.js');
225 | create_sqlite("test.sqlite");
226 | query_sqlite("test.sqlite");
227 | ```
228 |
229 | Run the Node.js app shows the following result.
230 |
231 | ```
232 | $ node db.js
233 | 1 : Bob McFett
234 | 2 : Angus Vader
235 | 3 : Imperator Colin
236 | ```
237 |
238 | ## HTTP request
239 |
240 | First, you must have the `sync-request` module installed in
241 | your Node.js setup. The Rust function will make synchronous HTTP requests through this module.
242 |
243 | ```
244 | $ npm i sync-request
245 | ```
246 |
247 | The Rust functions to access web services via HTTP/HTTPS and then save content on the local file system are as follows.
248 |
249 | ```
250 | use wasm_bindgen::prelude::*;
251 |
252 | #[wasm_bindgen]
253 | pub fn fetch(url: &str) {
254 | let content = nodejs_helper::request::fetch_as_string(url);
255 | nodejs_helper::console::log(url);
256 | nodejs_helper::console::log(&content);
257 | }
258 |
259 | #[wasm_bindgen]
260 | pub fn download(url: &str, path: &str) {
261 | let content = nodejs_helper::request::fetch(url);
262 | nodejs_helper::fs::write_file_sync(path, &content);
263 | }
264 | ```
265 |
266 | Build and create the `pkg` folder for the compiled `wasm` bytecode file and the Javascript shim file `my_project_name.js`.
267 |
268 | ```
269 | $ ssvmup build
270 | ```
271 |
272 | The Javascript calling program in Node.js looks like the following.
273 |
274 | ```
275 | const { fetch, download } = require('../pkg/nodejs_example.js');
276 |
277 | fetch("https://raw.githubusercontent.com/second-state/nodejs-helper/master/LICENSE");
278 | download("https://www.secondstate.io/", "test.html");
279 | ```
280 |
281 | Run the Node.js app shows the following result.
282 |
283 | ```
284 | $ node http.js
285 | https://raw.githubusercontent.com/second-state/nodejs-helper/master/LICENSE
286 | MIT License
287 |
288 | Copyright (c) 2020 Second State
289 |
290 | Permission is hereby granted, free of charge, to any person obtaining a copy
291 | ... ...
292 | ```
293 |
294 |
--------------------------------------------------------------------------------
/src/webassembly/run.md:
--------------------------------------------------------------------------------
1 | # Run a simple WebAssembly program
2 |
3 | In the previous article, we showed you how to compile a Rust program
4 | into a WebAssembly bytecode file.
5 | The easiest way to run a WebAssembly bytecode file is to simply load
6 | it into a web browser. Most modern web browsers already support
7 | WebAssembly.
8 |
9 | The code example below shows how to create a WebAssembly VM, load the
10 | bytecode application, export the Rust function in the bytecode into Javascript,
11 | and then call this Rust / WebAssembly function from Javascript.
12 | All those steps could be done in the browser's Javascript console.
13 |
14 | ```
15 | const response = await fetch('my_project_name.wasm');
16 | const buffer = await response.arrayBuffer();
17 | const module = await WebAssembly.compile(buffer);
18 | const instance = await WebAssembly.instantiate(module);
19 | const exports = instance.exports;
20 | const triple = exports.triple;
21 | ```
22 |
23 | In the browser Javascript console, you can now call the Rust `triple()` function
24 | in WebAssembly and triple your input number. The code below returns number `30`.
25 |
26 | ```
27 | triple(10);
28 | ```
29 |
30 | Of course, you can just easily load Rust compiled WebAssembly functions from
31 | your web page. Check out [a full example here](https://github.com/second-state/wasm-learning/tree/master/browser/triple).
32 |
33 |
34 |
--------------------------------------------------------------------------------