4

Including two versions of a Rust crate in a single project

 2 years ago
source link: https://reltech.substack.com/p/including-two-versions-of-a-rust?s=r
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

First, a little background…

Recently, I stumbled upon an obscure Rust development problem that I felt compelled to write about since I know others have or will be confused by it, as I was.

Currently, I’m working with several Rust Never Sleeps community members on a proof-of-concept (PoC) project writing a Raspberry Pi Pico WiFi driver in Rust. Since the Pico does not have WiFi hardware by default, we selected a daughterboard to work with from Pimoroni, the Pico Wireless Pack (which is really just a specialized ESP32-WROOM-32E module). Here’s a photo of a Pico and a WiFi board wired together on a breadboard.

I created an experimental branch of our PoC source tree attempting to use the latest version of a crate for a BME280 sensor module that’s used to sample temperature, pressure and humidity. This module recently went all-in on the embedded-hal-1.0.0-alpha.X series instead of the “more stable” 0.2.X series. So, I enabled the feature flag for rp-hal to use the newer embedded-hal version, and removed any references to the older 0.2.X series.

I reached a rustc compiler error that seemed straightforward, but I really struggled to understand and solve on my own (I’m still quite new to working with Rust at the time of writing):

   Compiling rp2040-hal v0.4.0
error[E0308]: mismatched types
    --> src/main.rs:1803:9
     |
1803 |         &MODE_0,
     |         ^^^^^^^ expected struct `embedded_hal::spi::Mode`, found struct `Mode`
     |
     = note: expected reference `&embedded_hal::spi::Mode`
                found reference `&Mode`
     = note: perhaps two different versions of crate `embedded_hal` are being used?

For more information about this error, try `rustc --explain E0308`.
error: could not compile `esp32-pico-wifi` due to previous error

And it’s very clear on what line this error originates, clearly from a method call to initialize a new rp2040_hal::Spi struct instance:

    let spi = spi.init(
        &mut pac.RESETS,
        clocks.peripheral_clock.freq(),
        8_000_000u32.Hz(),
        &MODE_0,
    );

As I’ve come to trust rustc’s error messages and can usually interpret them literally, I went searching for where I might be importing two different versions of the embedded_hal crate. This is where my confusion first began - I was no longer explicitly importing two versions of embedded-hal, but I noticed from studying my Cargo.lock file that the cortex-m crate was. And this is where I got very confused. I kept following different deadend paths trying to have my project only use the newer embedded-hal version. But no matter what I did, I struggled to resolve this problem.

Subscribe to the Relational Technologist newsletter so you don’t miss any future software engineering career advice, technical guides and training/workshop opportunities.

Better understanding of the problem

I decided that I needed some better background on the problem. So I joined the rp-hal Matrix chat server to get some help from the friendly community. They’ve been working with embedded-hal and Rust a lot longer than I have, so I was confident that they’d be able to help me learn better than my own experiments. And I was correct - they very quickly pointed me in the right direction to understanding the situation better, and provided a strong way of solving the issue.

The rustc compiler error I was experiencing was actually quite different than I expected. I actually need to use the two different versions of embedded-hal in my code since Rust considers data types to be different across crate major versions, even if these types share the exact same name. In my case, the type I’m working with to pass to Spi::init() is:

&embedded_hal::spi::Mode

This type is exactly the same name in embedded-hal-0.2.X as it is in 1.0.0-alpha.X. But as I learned, they’re not the same types to rustc. rp-hal still expects the 0.2.X MODE_0 type to be passed into init() and not the 1.0.0-alpha.X MODE_0 type.

A solution to the problem

So how did I solve this?

The answer is to import both major versions of embedded-hal and differentiate them by unique import names. This means that my Cargo.toml file changed from:

embedded-hal = { version = "0.2.5", features=["unproven"] }
embedded-hal-02 = { version = "0.2.7", package="embedded-hal"  }
embedded-hal = { version = "=1.0.0-alpha.7" }

Now in my Rust code, I can differentiate between the two versions by the names I assigned above in Cargo.toml:

use embedded_hal::digital::blocking::InputPin;

This makes use of InputPin from the 1.0.0-alpha.X version, while:

use embedded_hal_02::spi::MODE_0;

makes use of MODE_0 from the 0.2.X series of embedded-hal.

Conclusion

Once I received some better background on how Rust operates in this situation, it became clear that it’s not a complex problem to understand and ultimately solve. But until I understood this, it was a confusing situation. I hope this article helps you understand better when you’ve fallen into this situation, what Rust is expecting and ultimately how to solve it.

Every language has obscure edge cases that need a bit of nuance to understand how to solve, and this is one of Rust’s. But this is where community of other Rust developers becomes very important to engage with. I hope this article helps the Rust community work more effectively with our beloved language.

As always, thanks for reading and passing this along to others who would enjoy reading the Relational Technologist.

Please enjoy!

P.S. - Please join me and other Rust Never Sleeps community members in working to learn about embedded device development using Rust. Many of us started with no prior embedded development knowledge and experience, but are now confidently developing low-level Rust code on the Raspberry Pi Pico. You can too by joining us in community.

Join the Community


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK