0

I'm evaluating Rust as a possible replacement for C/C++ in a new iteration of embedded FW. As I create simple projects, potential inconveniences come up. It's important for this application that migration be relatively painless, meaning that the most idiomatic Rust approach to a design pattern may not be possible. But it is also valuable to learn those idioms, so I'd like to learn. With that background ....

In C++, it is easy to create default values for structures and allocate statically.

struct foo {
  int bar = 3;
  float baz = 10.0;
  char* msg = "hello there";
}

foo my_foo;

However, in Rust, getting default values for structure members seems to require using the Default::default() functionality. Consider this similar try:

struct MyStruct {
  a: i32,
  b: f32,
  c: bool,
}

impl Default for MyStruct {
  fn default() -> MyStruct {
      MyStruct {
          a: 3,
          b: 4.0,
          c: true,
      }
  }
}

static FOO: MyStruct = MyStruct { a:1, ..Default::default() };

So not only do I have to write a completely separate piece of code to create the defaults, but static allocation doesn't seem to be supported:

error[E0015]: cannot call non-const fn `<MyStruct as Default>::default` in statics
  --> src/main.rs:92:42
   |
92 | static FOO: MyStruct = MyStruct { a:1, ..Default::default() };
   |                                          ^^^^^^^^^^^^^^^^^^
   |
   = note: calls in statics are limited to constant functions, tuple structs and tuple variants

This all seems very awkward compared to C++. I understand the idioms may be different, but relatively straightforward migration from C++ is important in this application - more appropriate Rust idioms can be brought in over time. And also, having to write more code to accomplish what was done previously is not likely to go over well. Is there another approach to this problem?

Andrew Voelkel
  • 513
  • 4
  • 10
  • Can't you use either the lazy_static or the once_cell crates to defer static initialization to runtime? Or do you need it at compile time? – MeetTitan Nov 07 '22 at 06:15
  • "In C++, it is easy to create default values for structures and allocate statically" Well yes but this also creates a default, mandatory constructor call for every instance of that struct. Making your program start-up extra slow, so this is bad practice. Just one of numerous reasons why I wouldn't recommend using C++ for embedded systems. – Lundin Nov 07 '22 at 08:33

1 Answers1

2

The sticking point that you've hit is that static intializers must be const expressions but traits (like Default) do not have const support (yet). You'll need to implement a const function to construct your MyStruct without using Default:

impl MyStruct {
    pub const fn new() -> MyStruct {
        MyStruct {
            a: 3,
            b: 4.0,
            c: true,
        }
    }
}

static FOO: MyStruct = MyStruct { a:1, ..MyStruct::new() };

Or if this is common, you may want a dedicated constructor for it:

impl MyStruct {
    pub const fn with_a(a: i32) -> MyStruct {
        MyStruct {
            a,
            b: 4.0,
            c: true,
        }
    }
}

static FOO: MyStruct = MyStruct::with_a(1);
kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • I think it is preferred to have a `const` item and not `const fn`: `const EMPTY: MyStruct = MyStruct { ... };`. – Chayim Friedman Nov 07 '22 at 05:02
  • OK, I understand as much as I need to for now, but this is still highly inconvenient compared to a C++ feature which is used "all the time". These kind of things make it hard to sell a language. But thanks, it helps me understand. It would sure be nice if simple (const) constructors like this were automatically generated as in C++ – Andrew Voelkel Nov 07 '22 at 16:50
  • It occurs to me that, given the extensive macro support in Rust, it might be possible to automate constructor creation with a macro, *especially* once traits have const support. Does this makes sense? – Andrew Voelkel Nov 07 '22 at 17:18
  • @AndrewVoelkel I've written up an example that uses a macro to achieve C++-like syntax: [playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8bfa302c83cc52db283b9bbcaa5cb569). If you yourself are unfamiliar with Rust macros, this may be a bit daunting. And I'm sure you are aware, the more you strive for non-Rust syntax, you'll have less external resources to utilize and may make it harder to learn idiomatic Rust. But of course, that's up to you. – kmdreko Nov 07 '22 at 18:46
  • @kmdreko Thanks very much. I'm drinking from a firehose with Rust macros. I tried to do something like that for this (https://stackoverflow.com/questions/74341312/how-to-expose-entire-rust-structure-to-python-at-once), but just couldn't get it working. FWIW, I'm not striving for non-Rust *syntax*, I'm trying to create convenience features that don't exist so that I can get others to come along. Plus I would use them too. I think that is within the purview of the macro system? – Andrew Voelkel Nov 08 '22 at 19:35