Pake - Easily Build Lightweight Desktop Applications
Categories: Creation
A topic from last Thursday’s cross-platform technical sharing event at Juejin. I shared some thoughts and experiences from the open-sourcing process of Pake, and also discussed some of my ideas about front-end, Rust, open source, and technology productization. Interested friends are welcome to communicate, and please point out any unclear parts.
This article is divided into two parts. The first part is the Keynote and video of the sharing, and the second part is my sharing and learning notes, which will contain some more detailed knowledge about Rust.
Sharing Document
Use the left and right arrow keys to switch, and the full-screen button in the lower right corner of the PC to view in full screen. Move the mouse to the left sidebar to view the directory. The video is available on Youtube.
Q&A
Principles of Memory Management in Rust
-
In Rust, memory management is achieved through three concepts: Ownership, Borrowing, and Lifetimes. This mechanism allows Rust to guarantee memory safety without garbage collection.
-
Ownership: In Rust, every value has a variable that is called its owner. A value can only have one owner at any given time. When the owner (variable) goes out of scope, the value will be dropped.
-
Borrowing: In Rust, borrowing is a mechanism that allows you to access data without taking ownership of it. When you create a reference, you are borrowing data. Rust has two types of borrowing: mutable borrowing and immutable borrowing. Immutable borrowing is denoted by the
&symbol, and mutable borrowing is denoted by&mut. Immutable borrowing allows multiple references at the same time, but they can only read the data, not modify it. Mutable borrowing only allows one reference, but it can modify the data.
How to Understand Memory Safety and Thread Safety in Rust?
-
Memory Safety: Rust uses its ownership system and borrow checker to ensure memory safety. Every value in Rust has a variable called its “owner”. A value can only have one owner, and when the owner goes out of scope, the value is automatically cleaned up. This prevents issues such as null pointer dereferencing, double-frees, and dangling pointers. In addition, Rust’s borrow checker ensures the validity of references. At any given time, there can only be either one mutable reference (for reading and writing data) or multiple immutable references (for reading data), but not both simultaneously. This rule prevents data races and other concurrency-related memory errors.
-
Thread Safety: Rust’s ownership and borrowing rules also guarantee thread safety. Since at any given time, a value either has only one mutable reference or multiple immutable references, this ensures that data races do not occur even without locking. In addition, Rust’s type system and standard library also provide some concurrency abstractions, such as
Arc(atomic reference counting type) andMutex(mutex), which provide a safe way to share state between threads. -
Through these rules and tools, Rust can catch many common errors at compile time, allowing developers to write safer and more reliable code.
Introduce Rustc
-
Rustc is the official compiler for the Rust programming language. It compiles Rust source code into executables or libraries. It is written in Rust itself and uses an underlying technology called LLVM to generate machine code.
-
In actual Rust development, you use
cargo, the package manager and build tool, which automatically calls rustc to compile your code and handles other tasks such as downloading dependencies, running tests, and generating documentation.
Browser Engine in Tauri
-
Instead of directly integrating a separate Chromium environment for each application, Tauri directly uses the operating system’s built-in browser engine to execute Web apps. From a trend perspective, the built-in browser ecosystem of operating systems is continuously improving, and Tauri naturally benefits from these dividends.
-
Windows uses WebView2, which is based on Microsoft Edge and Chromium, and has been built into Windows 7 and later.
-
macOS uses WKWebView, which follows the macOS system version, similar to the effects in Safari.
-
Linux uses WebKitGTK, which is quite messy due to the many distributions.
-
Issues: 1. There are many bugs. Most of these bugs do not come directly from Tauri, but from the webview interfaces on various platforms. Therefore, the resolution cycle of these bugs is quite long, and it is currently difficult to use them directly in a production environment. 2. The documentation and community are not perfect, and most of the time you need to dig into the code yourself. 3. On some older devices, the time spent creating windows and webviews is still not negligible. 4. For front-end web pages, the platform differences have not been completely smoothed out, and front-end development still needs to consider platform compatibility.
Communication Explanation in Tauri
-
Advantages: This communication method allows you to leverage Rust’s performance and safety while enjoying the convenience and flexibility of JavaScript. You can assign computationally intensive tasks or tasks that require access to low-level APIs to Rust, and assign tasks such as UI and interaction logic to JavaScript.
-
Command Invocation: This communication method can be synchronous or asynchronous. In JavaScript, you can call functions defined in Rust using the
window.tauri.invokefunction. This function takes two arguments: the first argument is the name of the Rust function, and the second argument is the parameters passed to the Rust function. This function returns a Promise, which will be resolved when the Rust function has finished processing. This communication method is typically used for situations where immediate results are required. -
Event Communication: This communication method is asynchronous. In JavaScript, you can listen for events emitted from Rust using the
window.tauri.listenfunction. This function takes one argument, which is the name of the event. When the corresponding event is triggered, you can handle this event in JavaScript. Correspondingly, in Rust, you can emit events using thetauri::event::emitfunction. This communication method is typically used for tasks that run in the background or require a certain waiting time.
Why Combine WebAssembly and Rust?
-
Strong Performance: Rust is a systems-level programming language that provides low-level control, including memory management and thread management, allowing Rust code to run very fast. In addition, Rust’s design emphasizes zero-cost abstractions, meaning you can write high-level and abstract code, but the compiler will optimize it into low-level, highly efficient machine code. When this code is compiled into WebAssembly, this performance advantage can be passed on to web browsers.
-
Small Size: The Rust compiler (rustc) uses an LLVM-based compiler infrastructure, which can generate efficient, small code. When you compile Rust to WebAssembly, you can use specific compilation options and tools (such as
wasm-opt) to further compress and optimize the WebAssembly binary, making it more suitable for distribution over the network. -
Interoperability: Rust and WebAssembly are both designed to interoperate with other languages and technologies. Rust has a complete C FFI, making it easy to work with C libraries, which means you can eventually call C from the front-end. WebAssembly is designed to work with JavaScript and can even directly access the DOM in a web browser. Therefore, you can write high-performance algorithms in Rust and then call them from JavaScript.
-
Toolchain: Rust has a very powerful toolchain, including the
cargopackage manager and build system, andrustupfor managing Rust versions. For WebAssembly, the Rust team and community have created specialized tools and libraries, such aswasm-bindgenandwasm-pack, which make it easier to compile Rust to WebAssembly and call Rust functions from JavaScript. In addition, there are other tools, such aswasm-optandwasm-gc, that can help you optimize and reduce the size of WebAssembly files.
Rust & WebAssembly Peripheral Tools
-
wasm-bindgen: This is a Rust library and command-line tool used for interoperability between Rust and JavaScript. It generates the necessary glue code so you can directly call Rust functions from JavaScript, or JavaScript APIs from Rust.
-
wasm-pack: This is a command-line tool for building, testing, and publishing Rust-generated WebAssembly packages. It automatically calls
wasm-bindgento generate glue code and handles other tasks such as optimizing WebAssembly binaries and generating package description files (e.g.,package.json). -
wasm-opt: This is part of the
Binaryentool suite and is used to optimize WebAssembly binaries. It can reduce the size of WebAssembly files and improve runtime speed by removing unused code, reordering instructions, merging similar items, and so on. -
wasm-gc: This is a command-line tool used to collect and remove unused functions and data from WebAssembly binaries. This can help you reduce the size of WebAssembly files.
Firefox Uses Rust and WebAssembly to Improve Source Maps Performance
-
Encoding with Rust language, then compiling and packaging with WebAssembly to replace the performance-sensitive parts (parsing and lookup functions) in the original JavaScript, which is 6 times faster than the existing ones.
-
WebAssembly runs in binary form at the bottom layer of web browsers and can directly operate a large continuous storage buffer of bytes. The goal is to achieve or approximate the running speed of native instructions, with only a 1.5x difference compared to native instructions.
-
Due to the lack of a garbage collector, only programming languages without runtimes and garbage collectors can be compiled into WebAssembly, unless the controller and runtime are also compiled into WebAssembly. Rust is a safer and more efficient system programming language. Its memory management is safer and does not rely on garbage collection mechanisms, but instead allows you to apply for and release memory by statically tracking function ownership and borrowing. No extra work is required for compiling to WebAssembly.
-
Compared to C and C++, Rust libraries are easier to build, share, package, and extract common parts, and are self-documenting. Rust has a complete ecosystem such as rustup, cargo, and crates.io, similar to the Node.js ecosystem npm.
Cloudflare Compiles Rust to WASM and Calls it in Cloudflare Workers
-
This means that Cloudflare Workers support WebAssembly, and Rust developers can compile their code into WASM, upload it to their service data centers, and call these functions as easily as calling JavaScript functions.
-
In addition, wasm-pack allows you to compile Rust to WebAssembly and generate bindings between JavaScript objects and Rust objects.
Dropbox Compiles Rust to WASM to Easily Embed DivANS Codec into Web Pages
-
DivANS can be understood as a core compression technology of Dropbox, which saves 12% in compression ratio compared to zlib, and more than 2.5% compared to other algorithms at maximum settings.
-
Rust programs can be well embedded into any programming language that supports C Foreign Function Interface (FFI), and can even choose memory allocators at runtime through this C FFI. These features make it very easy to embed the DivANS codec into web pages using WASM, as shown above.
-
Foreign Function Interface (FFI) is a programming mechanism that allows a programming language to call functions or methods written in another programming language.
Shopify Uses Rust to Implement Template Engine, Compile to WASM, and Improve Running Efficiency
-
The original Liquid template engine was written in Ruby and ran on the server side. As the Shopify platform expanded, performance and security issues with this solution began to emerge.
-
To solve this problem, Shopify decided to rewrite the Liquid template engine in Rust and compile it into WebAssembly. This way, Liquid templates can run in the browser without running on the server side. This not only reduces the server load but also greatly improves the rendering speed of templates, which is a very valuable feature for web applications that need to handle a lot of user interaction and dynamic content.
Linux Kernel Adds Support for Rust as a Second Language
-
The Linux kernel is a huge and complex software project that has been using C as its primary programming language since its inception. However, some features of C, such as manual memory management and lack of type safety, make it more difficult to write correct and secure code.
-
Rust is a systems programming language whose design goal is to provide memory safety without garbage collection, making Rust very suitable for systems programming and embedded programming. Rust’s ownership model, borrow checking, and other features can catch many common errors at compile time, such as null pointer dereferencing and buffer overflows.
-
The idea of introducing the Rust programming language into the Linux kernel aims to improve the safety and reliability of kernel code through these features of Rust. This idea has been supported by many developers, including Linus Torvalds, the founder of Linux.
-
In fact, some components of the Linux kernel have begun to be written in Rust. For example, some members of Google’s Project Zero have begun to try to rewrite kernel components in Rust to solve some potential memory safety issues. In addition, the Rust programming language has been included in the Linux kernel source code tree as an optional programming language.