How to convert this C code?

Hi,

I have this C code which declare some configuration for an embedded radio chip: https://gist.github.com/Geobert/743fe8075c1fde1b4c7d2ef38a538094

I have a wrapper around the C library that manage the chip's radio and want to pass this construct to a configuration function (already bindgenerated).

This is where I am right now, but I can't make more progress:

Giving me this error:

error[E0080]: it is undefined behavior to use this value
  --> src\main.rs:39:1
   |
39 | / const PHY_INFO: [u32; 10] = [
40 | |     3,
41 | |     0x000F0F0F,
42 | |     0,
...  |
54 | |     (1 << 8) | 1,
55 | | ];
   | |__^ type validation failed: encountered a pointer, but expected plain (non-pointer) bytes
   |
   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior

Any idea?

Regards,

In Rust const is like #define. This value doesn't live anywhere. It's copy'n'pasted in places where it's used.

For global arrays you need static.

Thanks for your answer!

I've converted all consts to static and got:

error[E0277]: `*const u32` cannot be shared between threads safely
  --> src\main.rs:63:1
   |
63 | / static CHANNEL_CFG_ENTRY: RAIL_ChannelConfigEntry_t = RAIL_ChannelConfigEntry_t {
64 | |     phyConfigDeltaAdd: core::ptr::null(),
65 | |     baseFrequency: 915000000,
66 | |     channelSpacing: 1000000,
...  |
71 | |     attr: &CHANNEL_CFG_ENTRY_ATTR,
72 | | };
   | |__^ `*const u32` cannot be shared between threads safely
   |
   = help: within `rail_sys::RAIL_ChannelConfigEntry`, the trait `core::marker::Sync` is not implemented for `*const u32`
   = note: required because it appears within the type `rail_sys::RAIL_ChannelConfigEntry`
   = note: shared static variables must have a type that implements `Sync`

error[E0277]: `*const rail_sys::RAIL_ChannelConfigEntryAttr` cannot be shared between threads safely
  --> src\main.rs:63:1
   |
63 | / static CHANNEL_CFG_ENTRY: RAIL_ChannelConfigEntry_t = RAIL_ChannelConfigEntry_t {
64 | |     phyConfigDeltaAdd: core::ptr::null(),
65 | |     baseFrequency: 915000000,
66 | |     channelSpacing: 1000000,
...  |
71 | |     attr: &CHANNEL_CFG_ENTRY_ATTR,
72 | | };
   | |__^ `*const rail_sys::RAIL_ChannelConfigEntryAttr` cannot be shared between threads safely
   |
   = help: within `rail_sys::RAIL_ChannelConfigEntry`, the trait `core::marker::Sync` is not implemented for `*const rail_sys::RAIL_ChannelConfigEntryAttr`
   = note: required because it appears within the type `rail_sys::RAIL_ChannelConfigEntry`
   = note: shared static variables must have a type that implements `Sync`

The thing is that I want this code to be in the binary, which will be on the Flash memory of the chip, so it will not change anyway. It's just some configuration (weird) constructs that I'm trying to mimic in Rust.

Shall I keep them in C and use this instead? https://doc.rust-lang.org/nomicon/ffi.html#accessing-foreign-globals

It's a clash of two things:

  1. Global variables in Rust are accessible from all threads (single-threaded programs don't exist according to Rust's memory model), so Rust demands that their content is thread-safe (called Sync)

  2. Rust doesn't know what C does regarding thread safety, so pessimistically assumes that all raw (C) pointers are unsafe, and their access is not thread-safe, so structs containing them are not thread-safe either.

You have a few options:

  1. If your types can be accessed from multiple threads at the same time, without extra precautions or synchronization, tell Rust so by adding unsafe impl Sync for YourType {}
  2. If your types are not thread-safe, wrap them in a Mutex or use thread-local storage for them
  3. Replace *const Foo with &'static Foo which is non-null pointer to immutable memory. It's binary-compatible with C pointer, but tells Rust that it's thread-safe.

Thank you again,

While you were typing, I tried option 1. but it gives me:

error[E0080]: it is undefined behavior to use this value
  --> src\main.rs:39:1
   |
39 | / static PHY_INFO: [u32; 10] = [
40 | |     3,
41 | |     0x000F0F0F,
42 | |     0,
...  |
54 | |     (1 << 8) | 1,
55 | | ];
   | |__^ type validation failed: encountered a pointer, but expected plain (non-pointer) bytes
   |
   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior

I'm going to try option 3 :slight_smile: thanks again!

Option 3 didn't save me from the last error I posted above :frowning: Probably because of the transmutating :-/

I see you're also trying to make u32 out of a pointer, at compile time. This isn't supported yet:

For tracking of undefined behavior and uninitialized memory, at compile time pointers aren't integers, but a quite complex structure:

https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html

I've seen that yes, this is why I have the Transmuter, found on this tracking issue. So… there's no solution then? I have to keep this config struct in C and use https://doc.rust-lang.org/nomicon/ffi.html#accessing-foreign-globals ?

Can you initialize it in main? If so, you could make it static mut and write to it.

Maybe?
The doc of the function that will use this construction says:

 * @param[in] config A pointer to the channel configuration for your device.
 *   This pointer will be cached in the library so it must
 *   exist for the runtime of the application. Typically, this should be
 *   what is stored in Flash by the configuration tool.

So if the address of the struct does not move, it's ok to init it in main?

Oh, if you don't need a global symbol for it, then it's much easier.

Box::leak(Box::new(struct)) gives you a pointer that is valid for entire lifetime of the program and won't move.

Remember I'm in no_std world, I though Box uses the heap (which I don't have).

EDIT: "heap", not "head", which I'm losing anyway x)

If you borrow something in main(), it'll be close enough (the borrow will last until end of main, and while something is borrowed, it can't be moved). And static mut is still an option.

I'm trying static mut, thank you again!