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?

2
  • 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? Commented Nov 7, 2022 at 6: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. Commented Nov 7, 2022 at 8:33

1 Answer 1

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);
Sign up to request clarification or add additional context in comments.

5 Comments

I think it is preferred to have a const item and not const fn: const EMPTY: MyStruct = MyStruct { ... };.
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++
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?
@AndrewVoelkel I've written up an example that uses a macro to achieve C++-like syntax: playground link. 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 Thanks very much. I'm drinking from a firehose with Rust macros. I tried to do something like that for this (stackoverflow.com/questions/74341312/…), 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?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.