Tag Archives: Rust

Rust borrowing/lifetime & Modules

The connections between modules are the assumptions which the modules make about each other.

David Parnas
Information Distribution Aspects of Design Methodology, 1971

The Rust borrowing and lifetime concept help to express the assumptions being made between modules.

Defining an API, it is not about the signature of methods only, but also about the assumption about the usage and lifetime of the input and output data.

Rust Axum/Vue WebSocket Demo

EDIT: improved release of the demo is using VueJS with Element-Plus now

The following github project might be helpful for you.

https://github.com/frehberg/rust-vue-demo

The project builds a rust tokio/axum web-service binary, integrating a webui frontend based on Vue framework (npm based), and using websockets to push state-changes from web-service to the webui once per second.

The web-service backend is based on Rust Tokio-Axum framework.

The frontend is based on the “progressive” Vue JavaScript framework; npm is used to compile vue component files to static files (HTML/CSS/JavsScript). These files are embedded into the Rust executable. Further rendering is not performed by web-service during runtime. Any costly DOM tree manipulation is off-loaded into the web-browser. The Vue library Element-Plus provides the UI components.

A new Vue project can be set up following step by step the Vue Getting Started document and the the element-plus installation document; the element-plus starter repository provides a state of the art setup using Vue/Element-Plus (Note: using vite-packer instead of vue-packer or webpack).

The web-service has been implemented using Rust, and it will handle the HTTP requests and may return one of the embedded files or serve the websocket connection.

The compact Vue app realizes a dynamic/reactive web frontend, displaying the data being received via websocket from web-service.

Building the Demo Step by Step

At first clone the repository https://github.com/frehberg/rust-vue-demo into a local directory

> git clone https://github.com/frehberg/rust-vue-demo

After download, the npm project must be initialized at first, downloading the node.js dependencies.

> cd webui; npm install

Vue code and Rust code will be built together and executed using a single command, combining the cargo and npm build process. Now, invoke cargo to build and run the project. In the top directory invoke

> cargo run

The Vue application (web frontend) is defined by template file webui/src/App.vue, and the Vue component MessageMonitor.vue. The result of the npm-build-process is placed in folder webui/dist/. The Rust backend binary is defined by src/main.rs. The following image shows the complete source tree.

First, the build process will compile the Vue application (see build.rs), producing the files in folder webui/dist. Then the rust_embed macros in src/main.rs will embed the generated files of webui/dist/ and the rust compiler will generate the executable.

The web-service is self contained, the webui assets (Html and JavaScript files, images, etc.) are integrated into the binary.

When connecting with web-browser to host at port 3000, eg http://127.0.0.1:3000, the webui will be loaded and it will open a websocket ws://127.0.0.1/ws, receiving data from web-service, suitable for process monitoring. Once per second the service URL is sent to the UI for display.

Via the websocket the UI is able to send CAN frame messages to the web-service and is receiving CAN frames asynchronously. The source code of the reactive UI contains simply 100 lines of code.

This combination of rust-binary and Vue frontend (HTML/CSS/JavaScript) can be used either for backend server systems but is also suitable for embedded systems. The stripped release binary target/release/rust-vue will occupy ca. 4MB, containing the web-service backend, and the webui frontend (HTML/CSS/JavaScript files).

> RUSTFLAGS='-C link-arg=-s'  cargo build --release

The embedded files are static and sent to webui as is, without any kind of template engine processing; the costly DOM tree manipulation is off-loaded to the web-browser.

Solar Position Algorithm (SPA) release 0.3.0

The release 0.3.0 of the crate spa is fixing the flaw of the API, and updating to Rust edition 2021.

The SPA can be used to calculate solar position (azimuth and zenith_angle) and the time of sunrise and sunset. This data can be used to optimize the position of solar panels, to optimize load-cycles of energy storages, or to increase or reduce the lighting at home or in vehicles.

As for the flaw in the API: Just implementing unittests, I wasn’t aware that the members of the structure SolarPos lacked the declaration pub. Users of the lib were not able to read the result from return value! Now, after adding example files, these examples are verifying those members are publicly accessible, see below.

// ...
pub enum SunriseAndSet {
    PolarNight,
    PolarDay,
    Daylight(DateTime<Utc>, DateTime<Utc>),
}

// ...
pub struct SolarPos {
    // horizontal angle measured clockwise ...
    pub azimuth: f64,
    // the angle between the zenith and the center of the sun's disc
    pub zenith_angle: f64,
}

Moreover the bench tests caused additional complexity due to the required feature handling and conditional builds. For that reason the bench tests have been removed.

In the moment the bench API is getting stabilized any time in future, these bench test will be put back.

Lessons learned: Avoid superfluous complexity and ship with examples always!

Rust: signed vs unsigned arithmetic

A comment here https://stackoverflow.com/questions/12335784/c-integer-overflow postulated, the C-compiler would be able to optimize arithmetic expressions with undefined behavior (such as if (a -10 < 20) ) much better if the variable a being a signed integer (converting internally to an expression similar to a < 30). This is in contrast to the case variable a being an unsigned integer.

And indeed, there is a difference in the output of the C compiler (x86-64 clang 13.0.0), generating less instructions when declaring the variable a as signed integer int

Compared to the output of the C-compiler when declaring as unsigned integer unsigned int

Explaining the code generation of the C compiler

As for the programming language C, the behavior of overflow/underflow is undefined: Garbage In, Garbage Out!

This means the compiler is not constrained by having to worry about what happens when there is overflow/underflow. It only has to emit an executable that exemplifies the behavior for the cases which are defined. For example in case of an overflow of a single signed integer variable, then the behavior of the complete application is said to be undefined!

Analyzing code generation of the Rust compiler

I wondered if there might be any difference in the output of the rust compiler (using -C opt-level=3) either declaring variable a as type i32 or type u32.

Using the Compiler Explorer https://rust.godbolt.org, the conclusion is:
There is no difference! The Rust-Compiler does not transform the expression to something like a < 30 for the case using signed integers !

See the function using Signed Integer i32

See the function using Unsigned Integer u32

As it seems, this is not a lost opportunity for optimization of the signed case, but it is intended behavior, as these two statements are not equal in terms of integer underflow a - 10 < 20 and a < 30, where variable a being a signed integer. And for that reason Rust does not perform any optimization potentially changing the semantics of the expression.

For example, just assume a: i8 = -120 in this case the expression a - 10 would cause an underflow (wrapping) evaluating to value 126. In this case
a - 10 < 20 would yield false, but
a < 30 would yield true!!
So, whereas C is acting on the maxim “I think I know what you mean” and performing ruthless optimization, in contrast Rust is not changing the semantics when generating the binary code.

Now, to find these arithmetic expression with undefined behavior in your code, Rust clippy could be used. After installation of clippy, the following compiler attribute can be added to your lib or application

now calling cargo clippy the faulty location in question will be highlighted and could be corrected.

Conclusion

So, regarding arithmetic expression with undefined behavior, unlike C/C++ the Rust compiler is treating signed operations and unsigned operations the same. Those expressions with undefined behavior are not optimized by the compiler as this optimization might change the semantics of the code! Now, to find these spots in your Rust code, the compiler tool clippy can be used; either refactoring the code to get rid of the undefined behavior (see below), or handling these cases gracefully as shown in my previous post!

For example, using clippy the faulty expression a - 10 < 20 would be detected and could be changed to

PS: As dealing with acceptable and unacceptable input values, if you want to annotate functions and methods with “contracts”, using invariants, pre-conditions and post-conditions, check out the crate contractsDesign By Contract for Rust.

For example, using the crate contracts to define a pre-condition of the form “a >= 10“. Just, these constraints are not evaluated at compile time, but at runtime.