Bootstrap

魔改出一个 Encoder | Rust 学习笔记(一)

新年新目标

打算在 2021 年学习一门新的编程语言,Rust 是一个很好的标的,一方面它及其具备实用性;另一个方面它也能让我们在更高的层面上理解计算机。

本系列将是我从Rust小学生开始的Rust学习过程全记录。

话不多说,我们开整。

由于是一门新的语言(相对 Java),所以传统的到网上去找一本好的入门教材的方法失效了。

那我们就来康康 Rust 能做什么有趣的事情,有什么有趣的Repo。

Substrate(Polkadot公链)、Libra(Facebook链)、WeDPR(FISCO BCOS 隐私保护组件)都是用 Rust 写的,不过评估一下,这些 Repo 的难度太高了,不适合用来作为语言入门。

后来发现 Rust 在 WebAssembly 方面目前进展很不错:

WebAssembly是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如C / C ++等语言提供一个编译目标,以便它们可以在Web上运行。它也被设计为可以与JavaScript共存,允许两者一起工作。

>

## 简而言之

>

对于网络平台而言,WebAssembly具有巨大的意义——它提供了一条途径,以使得以各种语言编写的代码都可以以接近原生的速度在Web中运行。在这种情况下,以前无法以此方式运行的客户端软件都将可以运行在Web中。

所以,Rust 的学习路线就这么定下来了,从wasm开始!

检索实例

既然确定了目标,那么可以开始检索相应的实例。这个实例有两个条件:

  • 光有文章是不行的,必须配套相应的的源码

  • 这个源码必须足够简洁,适合用来入门

经过一番检索,最后找到了这个:

项目代码:

https://github.com/RodionChachura/rust-js-snake-game/

运行地址:

https://rodionchachura.github.io/rust-js-snake-game/

教程地址:

https://geekrodion.com/blog/rustsnake

git clone 下来,运行了试试,的确可以。

但感觉不是我想要的,因为前端代码的内容太多了。

然后打开官方教程:

https://developer.mozilla.org/zh-CN/docs/WebAssembly/Rusttowasm

看到:

Rust 和 WebAssembly 有两大主要用例:

>

- 构建完整应用 —— 整个 Web 应用都基于 Rust 开发!

- 构建应用的组成部分 —— 在现存的 JavaScript 前端中使用 Rust。

>

目前,Rust 团队正专注于第二种用例,因此我们也将着重介绍它。对于第一种用例,可以参阅 这类项目。

Yep,感觉我需要的是!

Yew 的探索之旅

首先找到 的官网:

Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly.

>

https://github.com/yewstack/yew

找到它官方的例子:

https://yew.rs/docs/zh-CN/getting-started/build-a-sample-app

结果,运行报错……

cargo-web is not compatible with web-sys.

遇到问题,第一时间,当然是到官方Repo里去检索啦,然后就搜到这么一条 Issue:

https://github.com/yewstack/yew/issues/1081

建议使用 trunk,妥~

Trunk 的探索之旅

跳转到 Trunk Repo:

https://github.com/thedodd/trunk

发现里面有examples,于是直接 clone 下来运行:

执行没问题,很好!

但是只有一个简单的实例,没法基于这个进行学习,怎么办?

我们回到 yew 的 Repo 里面,看下有没啥实例。

https://github.com/yewstack/yew/tree/master/examples

Examples 很多,也都能跑通,赞:

魔改出 Base64 Encoder!

在之前的编程课程里面,有一个编程要义我会经常提及:

在入门一个新的计算机技术的时候,千万不要一开始就从0到1!因为从0到1的难度对新手来说太高。最开始应该先去魔改一个已有的项目。

我选择的是todomvc,原始是长这样:

目的是把它修改成一个 Base64-Encoder:

Ok,那我们来看看原始代码:

......
    fn view(&self) -> Html {
        let hidden_class = if self.state.entries.is_empty() {
            "hidden"
        } else {
            ""
        };
        html! {
            

{ "todos" }

{ self.view_input() }
{ self.state.total() } { " item(s) left" }
    { for Filter::iter().map(|flt| self.view_filter(flt)) }
} } } ......

挺好,这个就是前端部分了,我们把它删减一下:

    fn view(&self) -> Html {
        let hidden_class = if self.state.entries.is_empty() {
            "hidden"
        } else {
            ""
        };
        html! {
            

{ "encode/decode" }

{ self.view_input() }
    { for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e)) }
} }

我们可以看到,输入的逻辑在这个地方,于是我们找到那个函数:

fn view_input(&self) -> Html {
        html! {
            // You can use standard Rust comments. One line:
            // 
  • /* Or multiline:
    */ } }

    再找到:

    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();
                }
    ......

    这个时候,我想先调试一下,因此需要把一些数据打印出来。

    这个时候,首先想到的是大法:

    println!("Input: {}", val);

    但是,在命令中,这个函数失效了!

    在和的 Repo 中进行检索,均未找到解决方案。

    但是随即发现有 Discord Chatroom,于是乎进去搜索聊天记录。

    Yummy,这里提到只要使用wasm-logger即可。

    https://crates.io/crates/wasm-logger

    在项目里添加:

    ......
    // in the first of main.rs
    #[macro_use] extern crate log;
    ......
    fn main() {
    		// init wasm logger!
        wasm_logger::init(wasm_logger::Config::default());
        yew::start_app::();
    }

    调用试试看:

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
            match msg {
                Msg::Add => {
                    info!("add things");
    ......

    妥了!

    接下来找到Rust Base64 的库,调用之(修改的地方用new标出了):

    ......
    use base64::{encode, decode};
    ......
    fn update(&mut self, msg: Self::Message) -> ShouldRender {
            match msg {
                Msg::Add => {
                    // new
                    info!("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,
                        };
                      	// new
                        info!("{}", entry.description);
                        self.state.entries.push(entry);
                    }
                    self.state.value = "".to_string();
                }

    运行之。

    Okay,Base64-Encoder就做好了!

    效果:

    Day1 的 Rust学习就到这里了。

    对了,最后长这样:

    [package]
    name = "encoder"
    version = "0.1.0"
    authors = ["Denis Kolodin "]
    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"