├── .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 | ![Build and Publish](https://github.com/second-state/rust-by-example-ext/workflows/Build%20and%20Publish/badge.svg) 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 | ![Run an example from the web page](src/rbeext.gif) 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 | ![Run an example from the web page](rbeext.gif) 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 | --------------------------------------------------------------------------------