41 comments

[ 4.3 ms ] story [ 99.4 ms ] thread
As a Rust fan and electronics enthusiast, I find these kinds of projects awesome. Thank you for the write up and keep on hacking!
Would xargo help with the cross compiler setup? https://github.com/japaric/xargo I've been meaning to look more at rust on ARM Cortex-M chips and recently saw tools like xargo that appear to make it a bit less tedious to setup.
Looks like it would help get libcore for your target more easily, yes.

Still, I'd like similar functionality to get in standard Cargo eventually.

This was quite entertaining! Having worked through many such build systems adventures myself, it's a genre I do appreciate. As always, the story is more captivating than the end result.

--

Also, yay! a shout out to my RFC 1133.

NB, Rust RFC 1133 "Make Cargo aware of standard library dependencies" isn't the same as IETF RFC 1133 "Routing between the NSFNET and the DDN".

I first searched for just "rfc 1133" and the matches didn't really make much sense :]

Rust RFC 1133: https://github.com/rust-lang/rfcs/pull/1133

Hmm, I feel obligated to read up on routing between the NSFNET and the DDN now.
This is by now a familiar sight:

  #[lang = "eh_personality"] extern fn eh_personality() {}
  #[lang = "panic_fmt"] extern fn panic_fmt() -> ! { loop {} }
But it's a little unsatisfying.

What does eh_personality actually do, and how would you write a 'real' implementation?

I can guess what panic_fmt is for, but exactly what is it supposed to do, and how would you write a 'real' implementation?

"exceptional handling personality": ask the DWARF people?

It should be removable with aborting panic.

I now have a "more real" panic_fmt in the repository:

    #[lang = "panic_fmt"]
    extern fn panic_fmt(details: ::core::fmt::Arguments, file: &'static str, line: u32) -> ! {
        println!("Panic at {}:{}, {}", file, line, details);
        let row_2 = ::gpio::Pin::output(::pins::ROW_2);
        let col_3 = ::gpio::Pin::output(::pins::COL_3);
        row_2.set_high();
        loop {
            col_3.set_low();
            ::busy_loop::wait_approx_ms(5);
            col_3.set_high();
            ::busy_loop::wait_approx_ms(200);
        }
    }
Note that println! is a macro I defined myself, it writes to the serial port.

I still don’t know about eh_personality, though.

Now that's a cute panic_fmt. :-)
wrt eh_personality, there's a good short answer with a link to a good (very!) long answer here:

http://stackoverflow.com/questions/16597350/what-is-an-excep...

So basically if I got it right, non-terminating panic_fmt and empty eh_personality means that we don't get stack unwinding at all, which also means that destructors don't get called. That could be fairly important for RAII heavy code.
You'd have to implement your own stack unwinding in a bare metal context, if you wanted it at all, yeah.
Is it important, when there is no other thread or process that could use any resource you're holding?
Resource wouldn't necessary be something internal in the application, it could be also some external system or something like that. You might want to close TCP connections cleanly in destructors, or maybe more relevantly for embedded systems the resource might be some actual physical gadget that is activated/deactivated.

Of course this is all hypotheticals and it could very well be argued that relying on destructors to do something actually critical would be a bad design.

> Of course this is all hypotheticals and it could very well be argued that relying on destructors to do something actually critical would be a bad design.

Eh, I'd only argue that in the case of garbage collected languages. In RAII languages like Rust and C++, it's the preferred way.

There's a classic interview question about RAII along the lines of "in what situation can a RAII resource be acquired, but not released?". The key answer is "if someone turns the power off".

panic_fmt entering an infinite loop leads to pretty similar results as someone turning the power off. The stack will not be unwound, destructors will not be run, and bad designs will leave external resources in an inconsistent state.

I mean, sure.

loop {}

^--- That'll also cause destructors to not be run.

> "Rust on BBC micro:bit"

OT: I just did a double-take. For a moment there, when I saw the title, I thought of a rusty computer part.

glad I wasn't the only one.
These micro:bit look pretty interesting from an embedded controller perspective - does anybody have any useful comparisons between them and others, eg Arduino etc?

I've been working on a project where I will end up having to make a simple stepper motor to drive an analogue clock and I think I can use this to control it (advance x% of turn every minute, currently x=50) though I was initially thinking of a small PICAXE circuit as we have some equipment at the school I work at. I like the thought that I could access it over BLE though, and that it could be programmed to handle DST automatically and even drive a few LEDs for good measure

Also, what is the power consumption? As my project involves a clock, I'm thinking it needs to last a while on some batteries, though at least its big enough that I can pack a few in.

Just get a bog-standard STM32 board for very-cheap.... don't bother with one of these.

e.g. A standard STM32F103 board can be had for around $3.

Hm, thanks.. I will look closer at this kind of thing over my holidays. That looks like I can program it over RS-232 and operates at battery voltages, which is really what I require.
Did he mention he integrated this with the mbed ? i though that was hard, because of c++.

But if he did so, that would be really valuable - it's a good solution for the problems of portability and software support , mnajor issues for embedded developers.

Yes, I've had Rust code and C++/mbed code in the same program. But C++ is still an issue, I wrote a C wrapper for the one C++ method I was using. Doing so for all of mbed would be very tedious and fragile.

Servo's fork of rust-bindgen https://github.com/servo/rust-bindgen has some C++ support and might be able to generate bindings for all(?) of mbed (at least for a given hardware configuration), but I haven't tried.

Very cool, and very persistent. Good job!

I can see myself losing a month on something like this.

<rant> Now, I like the tech and hacker side of it, but it made me think again of how complicated modern hardware and software have become compared to staying with plain C, not even C++. This is not wholly due to more complicated hardware, but so many people building stuff without going back to first principles. I think the Rust crowd are doing their homework here though.

I understand the benefits of Rust, since I am learning it at the moment, but I guess at my age it is a harder sell for something like this micro. It is so easy to just drop into C for small micros. The BBC micro:bit has a C/C++ interface, of which I bet the C interface is cake. It's small enought that all a good programmer has to do is dot their 'i's and cross their 't's anyway (or they should). </rant>

It's become more complicated because C is much more cumbersome to write than a higher-level language, so everyone prefers the latter. If I'm working on a project for fun on my spare time, I don't want to spend an hour fighting the compiler figuring out why array bounds are being weird, or why the compiler is complaining about the type, or whatever.

Python is much more do-what-I-mean, for example, and it's much more appealing to me when I want to write something quickly. I've tried other languages and, with the exception of Rust, I always feel like development shouldn't have to be this yak-shavy.

I've written a good amount of rust, and a ton of C/C++, and, well, the compiler complaining about the type, or, well, anything really, is far more likely in Rust than C or C++
That's a feature -- not a bug.
It is, but there's usually an easier (or more obvious) way to fix it in Rust.
Doesn't the compiler only complain about the type when you're doing something wrong anyway?

C is only cumbersome when you don't already have the data structures you want implemented, which is why there are usually many libraries for any one data structure you want to represent, and operations on it.

As for array bounds, it should work so long as you don't access out of bounds, and you can do that by making sure you know what you're accessing. It's not that hard - but even if you want, you can get your compiler to tell you when you do out of bounds access at runtime.

> As for array bounds, it should work so long as you don't access out of bounds, and you can do that by making sure you know what you're accessing. It's not that hard

Judging by the flood of out of bounds array accesses in shipping code over several decades, actually it is pretty hard to consistently get it right.

> even if you want, you can get your compiler to tell you when you do out of bounds access at runtime.

At the cost of making your program slower than Java. So at that point why not use a memory safe language?

> C is only cumbersome when you don't already have the data structures you want implemented, which is why there are usually many libraries for any one data structure you want to represent, and operations on it.

I suspect this is the mistake I'm making, because 90% of my time is spent writing code to do data structure manipulation. I'll look around and try to find some libraries for things, but I'm usually constrained to the ecosystem of the particular flavor of C I write (for the Arduino, which is really C++, but still). I guess most general-purpose libraries will work there too, though.

> As for array bounds, it should work so long as you don't access out of bounds, and you can do that by making sure you know what you're accessing.

No, they do work, it just takes me ages to find the correct thing to tell the compiler so it understands what I want to do. Don't get me started on the ten types of string, though :(

If you're writing C code, the right way to think about data structures in a lot of cases is that they're an optimization.

You can implement a map using an array of struct { key, value }, and O(n) search. If you keep it sorted, that becomes O(n log n). A hash table is a further optimization.

Most of the time you don't need these.

I used to think similarly to you about C, or even Python for that matter, but after years of leaving C, I am back at it. This is while also learning Rust. Why? Because the easy languages are never self-contained to perform a specialized task, and you wind up going through dependency hell, or rolling your own libraries. C takes work, but you stay in the same arena while sussing it out. And, in the end, it is truly portable and usually the fastest of any other implementation. Horses for courses I guess.
I don't understand how using C lets you avoid dependency hell or rolling your own libraries. This comment doesn't make sense to me - dependency management/rolling your own libraries seems to be there in all languages.
I am specifically referring to it in the context of the OP about an embedded device. For general computing, your objections may hold. In all my years of doing small devices, Python's libs wouldn't fit in the space of the unit if you could find the exact library to run on different architectures.

The only other languages I have used aside from C for the small devices is Forth or assembler. There are usually ready-made libs or interfaces from the manufacturer. I haven't seem much provided in Python or other high-level languages. The NodeLua project is an exception, and it is promising. I have already began work on a Lisp of mine (written in C) for this device. Others have done so too.

I guess if you have a Raspberry Pi or other, you can afford the space, and it is basically a small Linux box, so you can run anything you like. I was sticking to a discussion of small, embedded devices where those are not yet options. In the future, I am sure they will be big enough to fit high-level languages.

> And, in the end, it is truly portable and usually the fastest of any other implementation

It definitely is, and, when I need these things, I will write the program in C. It's very rare for me.

What devices are you writing for, and what type of app? I was programming my Pic chips in assembler in the 90s, and C was high-level at that point ;) I needed C for the other chips because I was running timing-critical apps for high-speed CNC implementations.