Author: Li Dagou (Li Aohua)/Post editor: Zhang Handong
All source code of this series:
Github.com/leeduckgo/R…
New Year’s resolutions
Planning to learn a new programming language in 2021, Rust is a good target because it is extremely practical. On the other hand, it allows us to understand computers at a higher level.
This series will be a complete record of my Rust learning process from Rust primary school onwards.
Without further ado, let’s turn it round.
Because it’s a new language (as opposed to Java), the traditional approach of going online to find a good introductory textbook doesn’t work.
So let’s see what fun things Kangkang Rust can do and what fun Repo there is.
Substrate (Polkadot public chain), Libra (Facebook chain), and WeDPR (FISCO BCOS Privacy protection component) are all written in Rust, but upon evaluation, these REPOs are too difficult to be used as a language primer.
It turns out Rust is doing pretty well with WebAssembly:
WebAssembly is a new way of coding that can run in modern Web browsers – it is a low-level assembler like language with a compact binary format that runs close to native performance and provides a compilation target for languages such as C/C ++ so that they can run on the Web. It is also designed to coexist with JavaScript, allowing the two to work together.
In a nutshell
WebAssembly is huge for Web platforms — it provides a way for code written in a variety of languages to run on the Web at near-native speeds. In this case, client software that could not run this way before will be able to run on the Web.
So Rust’s learning path was set, starting with WASM!
Retrieve instance
Now that you have identified the target, you can begin retrieving the corresponding instance. This instance has two conditions:
- Light article is not, must support the corresponding source
- The source code must be concise enough to get started
After some searching, I found this:
Project Code:
Github.com/RodionChach…
Operating address:
Rodionchachura. Making. IO/rust – js – sna…
Tutorial Address:
Geekrodion.com/blog/rustsn…
Git Clone
But it didn’t feel like what I wanted because there was so much front-end code.
Then open the official tutorial:
Developer.mozilla.org/zh-CN/docs/…
See:
Rust and WebAssembly have two main use cases:
- Build a complete Application – The entire Web application is built on Rust!
- Build application components – Use Rust in an existing JavaScript front end.
For now, the Rust team is focusing on the second use case, so we’ll focus on that as well. For the first use case, see projects like Yew.
Yep, feels like I need Yew!
Yew’s journey of discovery
Find yew’s website first:
Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly.
Github.com/yewstack/ye…
Find the official example:
Yew. Rs/docs/useful – CN /…
As a result, an error was reported…
cargo-web is not compatible with web-sys.
Copy the code
When you encounter a problem, the first time, of course, is to search the official Repo, and then find such an Issue:
Github.com/yewstack/ye…
It is recommended to use trunk
Trunk’s journey of discovery
Jump to Trunk Repo:
Github.com/thedodd/tru…
Clone examples:
Execution ok, good!
But what if you only have a simple example and you can’t learn from it?
Let’s go back to Yew’s Repo and see if there are any instances.
Github.com/yewstack/ye…
There are many Examples, and all of them can run.
Base64 Encoder!
When learning a new computer technology, don’t start from zero to one! Because the difficulty of going from 0 to 1 is too high for beginners. Start by demonizing an existing project.
I chose todomvc, the original looks like this:
The purpose is to change it to a base64-encoder:
Ok, let’s look at the original code:
.fn view(&self) -> Html {
let hidden_class = if self.state.entries.is_empty() {
"hidden"
} else {
""
};
html! {
<div class="todomvc-wrapper">
<section class="todoapp">
<header class="header">
<h1>{ "todos" }</h1>
{ self.view_input() } </header> <section class=classes! ("main", hidden_class)>
<input
type="checkbox" class="toggle-all" id="toggle-all" checked=self.state.is_all_completed() onclick=self.link.callback(|_| Msg::ToggleAll) /> toggle-all" /> todo-list"> { for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e)) } footer", hidden_class)> todo-count"> { self.state.total() } { " item(s) left" } filters"> { for Filter::iter().map(|flt| self.view_filter(flt)) } clear-completed" onclick=self.link.callback(|_| Msg::ClearCompleted)> { format! ("Clear completed ({})", self.state.total_completed()) } info"> { "
Double-click to edit a todo" } { "
Written by " }<a href="https://github.com/DenisKolodin/" target="_blank">{ "Denis Kolodin" }</a></p>
<p>{ "Part of " }<a href="http://todomvc.com/" target="_blank"> {"TodoMVC" }</a></p>
</footer>
</div>
}
}
}
......
Copy the code
Ok, that’s the front end, let’s cut it out:
fn view(&self) -> Html {
let hidden_class = if self.state.entries.is_empty() {
"hidden"
} else {
""
};
html! {
<div class="todomvc-wrapper">
<h1>{ "encode/decode" }</h1>
{ self.view_input() } <section class=classes! ("main", hidden_class)>
<ul class="todo-list">
{ for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e)) }
</ul>
</section>
</div>
}
}
Copy the code
We can see that the input logic is in view_input(), so we find the function:
fn view_input(&self) -> Html {
html! {
// You can use standard Rust comments. One line:
// <li></li>
<input
class="new-todo"
/ / change replaceholder
placeholder="What needs to be encode/decode?"
value=&self.state.value
oninput=self.link.callback(|e: InputData| Msg::Update(e.value))
onkeypress=self.link.batch_callback(|e: KeyboardEvent| {
if e.key() == "Enter" { Some(Msg::Add) } else { None }
})
/>
/* Or multiline:
*/}}Copy the code
Msg::Add:
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Add => {
//info! ("add things");
let description = self.state.value.trim();
let description_handled = format!("{}: {}", description, encode(description.to_string()));
if! description.is_empty() {let entry = Entry {
description: description_handled,
completed: false,
editing: false};//info! ("{}", entry.description);
self.state.entries.push(entry);
}
self.state.value = "".to_string(); }...Copy the code
At this point, I want to debug first, so I need to print out some data.
Print = print = print
println!("Input: {}", val);
Copy the code
However, in the Trunk serve command, println! This function is invalid!
A search in trunk and Yew’s Repo failed to find a solution.
But then I noticed that Yew had a Discord Chatroom, so I went to search for the chat logs.
Yummy, as mentioned here just use wASM-Logger.
Crates. IO/crates/wasm…
Add wASM-Logger to your project:
.// in the first of main.rs
#[macro_use] extern cratelog; .fn main() {
// init wasm logger!
wasm_logger::init(wasm_logger::Config::default());
yew::start_app::<Model>();
}
Copy the code
Try calling:
fn update(&mut self, msg: Self::Message) -> ShouldRender {
matchmsg { Msg::Add => { info! ("add things"); .Copy the code
Completed!
Next find the library in Rust Base64 and call it (where modifications are marked with new) :
.usebase64::{encode, decode}; .fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Add => {
// newinfo! ("add things");
let description = self.state.value.trim();
// new
let description_handled = format!("{}: {}", description, encode(description.to_string()));
if! description.is_empty() {let entry = Entry {
// new
description: description_handled,
completed: false,
editing: false};// newinfo! ("{}", entry.description);
self.state.entries.push(entry);
}
self.state.value = "".to_string();
}
Copy the code
Run.
Okay, base64-encoder is ready!
Effect:
Cargo. Toml ended up looking like this:
[package]
name = "encoder"
version = "0.1.0 from"
authors = ["Denis Kolodin <[email protected]>"]
edition = "2018"
[dependencies]
strum = "0.20"
strum_macros = "0.20"
serde = "1"
serde_derive = "1"
yew = { path = "./packages/yew" }
yew-services = { path = "./packages/yew-services" }
log = "0.4.6"
wasm-logger = "0.2.0"
base64 = "0.13.0"
Copy the code
The public and private keys and address of ETH are generated
All source code of this series:
Github.com/leeduckgo/R…
This is the second installment of Rust’s learning Notes. In the first post, we created an Encoder, and now we’re going to continue on our path to a +1 difficulty Repo:
Rust library for generating cryptocurrency wallets
Github.com/AleoHQ/wagy…
Changed target 0x1:
Extract the Repo ethereum private key, public key, address generated part, print to the console.
But before the magic change, the author first of all to the last article a little supplement, summarize the knowledge points involved in the last article.
The knowledge points covered in the previous article
- Assignment of a variable
- format! Function (connection string)
- Add and use library, take wASM-Logger as an example
- Trunk combines with Yew to wasm Rust programs and make them accessible in browsers
Run the wagyu again
The first thing to do is verify that the library meets our needs, so follow the Readme in the Repo and run through it as source code.
# Download the source code
git clone https://github.com/AleoHQ/wagyu
cd wagyu
# Build in release mode
$ cargo build --release
./target/release/wagyu
Copy the code
Success:
In the process, we learned more about cargo:
$ cargo run # direct execution
$ cargo build The executable file is in./target/debug
$ cargo build --release The executable file is under./target/release
Copy the code
Study the Wagyu code
First glance at the directory structure:
.Bass Exercises ── AUTHORS Bass Exercises ── Cargo.Lock Bass Exercises ── Cargo.Toml Bass Exercises ─ License-Apache Bass Exercises ─ License-mit Bass Exercises ── Bitcoin Bass Exercises ─ Ethereum ├ ─ ─ model ├ ─ ─ monero ├ ─ ─ target ├ ─ ─ zcash └ ─ ─ wagyu ├ ─ ─ cli │ ├ ─ ─ bitcoins. Rs │ ├ ─ ─ ethereum. Rs │ ├ ─ ─ mod. Rs │ ├ ─ ─ Monero. Rs │ ├ ─ ─ the parameters │ └ ─ ─ zcash. Rs ├ ─ ─ lib. Rs └ ─ ─ main. RsCopy the code
As we can see, the main entrance is Wagyu.
In Wagyu’s main.rs, submodules in the CLI directory are called, and then submodules at the level of the CLI are called.
The code is as follows:
fn main() - >Result<(), CLIError> {
let arguments = App::new("wagyu")
.version("v0.6.3")
.about("Generate a wallet for Bitcoin, Ethereum, Monero, and Zcash")
.author("Aleo <[email protected]>")
.settings(&[
AppSettings::ColoredHelp,
AppSettings::DisableHelpSubcommand,
AppSettings::DisableVersion,
AppSettings::SubcommandRequiredElseHelp,
])
.subcommands(vec![
BitcoinCLI::new(),
EthereumCLI::new(),
MoneroCLI::new(),
ZcashCLI::new(),
])
.set_term_width(0)
.get_matches();
match arguments.subcommand() {
("bitcoin".Some(arguments)) => BitcoinCLI::print(BitcoinCLI::parse(arguments)?) , ("ethereum".Some(arguments)) => EthereumCLI::print(EthereumCLI::parse(arguments)?) , ("monero".Some(arguments)) => MoneroCLI::print(MoneroCLI::parse(arguments)?) , ("zcash".Some(arguments)) => ZcashCLI::print(ZcashCLI::parse(arguments)?) , _ = >unreachable!(),}}Copy the code
If you go to the Ethereum. rs directory again, there is a simple function:
pub fn new<R: Rng>(rng: &mut R) -> Result<Self, CLIError> {
letprivate_key = EthereumPrivateKey::new(rng)? ;let public_key = private_key.to_public_key();
letaddress = public_key.to_address(&EthereumFormat::Standard)? ;Ok(Self {
private_key: Some(private_key.to_string()),
public_key: Some(public_key.to_string()),
address: Some(address.to_string()),
..Default::default()
})
}
Copy the code
That’s good. I’ll take it!
Copy the necessary files to the new project
- New project
$ cargo new hello-crypto-rust
Copy the code
Or make a copy of the previous project.
- the
wagyu
theCargo.toml
Copy the necessary content in
[dependencies]
log = "0.4"
pretty_env_logger = "0.3"
wagyu-ethereum = { path = "./ethereum", version = "0.6.3" }
wagyu-model = { path = "./model", version = "0.6.3" }
arrayvec = { version = "0.5.1" }
base58 = { version = "0.1" }
clap = { version = "~ 2.33.1" }
colored = { version = "1.9" }
digest = { version = "0.9.0" }
either = { version = "1.5.3" }
failure = { version = "0.1.8" }
hex = { version = "0.4.2" }
lazy_static = { version = "1.4.0" }
rand = { version = "0.7" }
rand_core = { version = "0.5.1" }
safemem = { version = "0.3.3" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
tiny-keccak = { version = "1.4" }
[profile.release]
opt-level = 3
lto = "thin"
incremental = true
[profile.bench]
opt-level = 3
debug = false
rpath = false
lto = "thin"
incremental = true
debug-assertions = false
[profile.dev]
opt-level = 0
[profile.test]
opt-level = 3
incremental = true
debug-assertions = true
debug = true
Copy the code
- the
ethereum
withmodel
Copy two folders tohello-crypto-rust
directory
The file directory looks like this:
.├ ── Cargo. Lock ├── Cargo. Toml ├── Ethereum ├─ SRC ├─ targetCopy the code
Add the code
- supplement
lib.rs
file
Create a new lib.rs file in the SRC directory with the following contents:
pub extern crate wagyu_ethereum as ethereum;
pub extern crate wagyu_model as model;
extern crate pretty_env_logger;
Copy the code
Crate loads the external crate.
Wiki.jikexueyuan.com/project/rus…
- write
main.rs
File.
First reference the necessary external modules:
use rand::{rngs::StdRng};
use rand_core::SeedableRng;
use hello_crypto_rust::ethereum::{EthereumPrivateKey, EthereumFormat};
use hello_crypto_rust::model::{PrivateKey, PrivateKeyError, AddressError, PublicKeyError, PublicKey};
#[macro_use] extern crate log;
Copy the code
Then we write the main function:
fn main(){
pretty_env_logger::init(); // Initialize the pretty_env_logger module
new(); // Call new
}
Copy the code
Write the new() function:
pub fn new() - >Result<EthereumPrivateKey, CreateError> {
let rng = &mut StdRng::from_entropy();
letprivate_key = EthereumPrivateKey::new(rng)? ; info! ("priv: {}", private_key.to_string());
letpublic_key = private_key.to_public_key(); info! ("pub: {}", public_key.to_string());
letaddress = public_key.to_address(&EthereumFormat::Standard)? ; info! ("addr: {}", address.to_string());
Ok(private_key)
}
Copy the code
So we’re using this relative to println! More advanced output, through log output.
Here’s the key grammatical sugar –? For error handling.
Connecting result to match looks ugly; Luckily,? Operators can make this logic nice and clean. ? The operator is used after an expression that returns a Result value and is equivalent to a matching expression where the Err(Err) branch expands into a pre-returned return Err(Err) and the Ok(Ok) branch expands into an Ok expression.
— rustwiki.org/zh-CN/rust-…
Two equivalent functions, one using? None of them:
fn not_use_question_mark() {
let a = 10; // If you change this to 9, you will get an error.
let half = halves_if_even(a);
let half = match half {
Ok(item) => item,
Err(e) => panic!(e),
};
assert_eq!(half, 5);
}
fn use_question_mark<'a> () - >Result<i32, &'a str> { // We must return Result
let a = 10;
lethalf = halves_if_even(a)? ;/ / for? The function must return Result
assert_eq!(half, 5);
Ok(half)
}
Copy the code
Next, we define the enumerated type CreateError, which includes AddressError, PrivateKeyError, and PublicKeyError.
pub enum CreateError {
AddressError(AddressError),
PrivateKeyError(PrivateKeyError),
PublicKeyError(PublicKeyError)
}
impl From<AddressError> for CreateError {
fn from(error: AddressError) -> Self {
CreateError::AddressError(error)
}
}
impl From<PrivateKeyError> for CreateError {
fn from(error: PrivateKeyError) -> Self {
CreateError::PrivateKeyError(error)
}
}
impl From<PublicKeyError> for CreateError {
fn from(error: PublicKeyError) -> Self {
CreateError::PublicKeyError(error)
}
}
Copy the code
Try It!
Achieve success:
The knowledge points covered in this article
- More uses of cargo
lib.rs
The use of the- Function and function return value
pretty_env_logger
The use of the- Enumeration type to
CreateError
As an example
About the author:
Li Dagou (Li Haohua), deputy Director of Blockchain Technology and Application Research Center of Shanghai University of International Business and Economics, CTO of Bochian Education, Blockchain certification lecturer of FISCO BCOS (WeBank Blockchain Framework), 5-year blockchain engineer, master of Peking University. Research areas include: blockchain systems, consensus mechanisms, smart contracts, blockchain applications, digital identities, etc.
Content: February issue of Rust Chinese (Rust_Magazine)