Most active commenters
  • neonsunset(4)
  • leoedin(3)

←back to thread

205 points michidk | 19 comments | | HN request time: 0.612s | source | bottom
1. nsoonhui ◴[] No.41835659[source]
I'm ignorant about Rust, but to me it's just static type language akin to C#. And C# has IOT library which seems to target Rust most usual use case, namely on embedded platform. C# also has memory safety just like Rust.

So why do we need Rust at all? What's the use case for it?

Anything that I'm missing?

replies(5): >>41835666 #>>41835762 #>>41836656 #>>41839000 #>>41843390 #
2. 15155 ◴[] No.41835666[source]
> Anything that I'm missing?

C# is garbage collected. This is a no-go in many/most embedded software applications.

C# also grants you poor explicit control over heap/stack allocation: this is essential for embedded development.

replies(3): >>41835721 #>>41838112 #>>41838658 #
3. nlitened ◴[] No.41835721[source]
Is it really true though? I don't have much knowledge about embedded development, but as far as I know, Java has been used in many embedded systems, and was running on phones' SIM-cards. Please correct me if I am wrong.

I think that most modern embedded systems are nowadays more powerful than my first desktop computer — is it really still worth for the majority of embedded projects to count every byte at the expense of developers' productivity (and overall project success, as a result)?

replies(2): >>41835786 #>>41835829 #
4. leoedin ◴[] No.41835762[source]
Rust compiles to native machine code. C# compiles to bytecode which runs in a runtime environment. That adds a huge amount of overhead, and means you don't have direct access to the hardware registers. The .net IoT framework is targeting computers running operating systems.

When you compile rust for an embedded target it's running "bare metal" - there's no operating system, the microcontroller literally just starts running the instructions your compiler put at memory address 0. If you want anything an operating system offers - task scheduling, memory allocation, file system access, network access etc - you have to compile it into the binary that's loaded into the microcontroller.

Only languages which compile directly to machine code are really suitable for that - C, C++, Rust (plus a long list of less common ones - this list of languages which can compile using LLVM is a good place to start https://github.com/learn-llvm/awesome-llvm?tab=readme-ov-fil...)

replies(1): >>41838621 #
5. leoedin ◴[] No.41835786{3}[source]
SIM cards are a weird exception because they have a hardware computer designed to run a subset of Java. Normally Java compiles to a bytecode which is run by the Java VM - in SIM cards the VM is a hardware implementation of the Java VM. That's super uncommon - most processors in the world have a different instruction set, like ARM or RISC or x86. Java can't compile to instructions in those languages. Rust can.

Yes, you can run a VM on one of those processors in which you run a language like C# or Java. Look at MicroPython as a successful example. But the code which runs that VM has to be written in something. Typically that has been C and C++ - but Rust can also do it.

You're right though - a lot of what we call "embedded computing" could be done using a modern VM interpreted language. There are some languages out there - MicroPython and Squirrel come to mind - which can run on the memory and storage constrained environment of a microcontroller. The mainstream implementations of Java and C# use way to much memory - they've been optimised for desktop environments. Microcontrollers often have 64 - 1024 kB of RAM.

You could ship products with application processors like the Raspberry Pi, running an OS, and write your application in whatever language you like. But that costs 1-2 orders of magnitude more money. A cheap microcontroller is $1. By the time you've added the RAM, flash and supporting power rails, a cheap application processor is $10+. Then you have to maintain an OS - that's a lot of engineering time. Sometimes it's worth it, sometimes it's not - the tradeoff depends on the product.

6. psychoslave ◴[] No.41835829{3}[source]
Not sure how well it fits reality, but it’s also to a large extend how dramatic can be the result of a fault of your system.

If it’s embedded in a coffee machine, maybe all cost-effective taken into account it’s ok to have an over-bloated software stack maintained by the cheapest folk the manager could found.

Now if you are working on embedded software for some vehicle like a critical part of a car, a train or a spaceship, considerations of safety for both ethic and legal reasons might lead to different tradeoffs and conclusions.

7. lionkor ◴[] No.41836656[source]
You're missing that Rust has safety in terms of multithreading, which C# doesn't have. Writing a data race in C# is the default, writing thread safe code takes work. In Rust, the default is that it's safe, you have to jump through a lot of hoops to try to make it not thread safe. The same is true for async, which in C# is also a problem.

In all C# codebases I've seen, you have threads and tasks, and you often run into multiple threads or tasks holding a mutable reference to the same data. That's not legal in Rust without synchronization/locking.

If you don't believe me, just spin up a new main.rs file and write code that has a data race.

replies(2): >>41838790 #>>41839112 #
8. unnouinceput ◴[] No.41838112[source]
I would argue that using JSON in embedded, especially on ESP32, is another no-go. I'd wage that using a binary format and later on, after you're out of ESP32, convert it to JSON would be a bigger performance gain than the evangelic tone of this article vis-a-vis of Rust.
9. neonsunset ◴[] No.41838621[source]
- https://nanoframework.net/

- https://www.wildernesslabs.co/device

Here are examples where C# is successfully used as a language for an embedded target.

In addition to that, compiling to bytecode is just one way to execute it out of many, and the statement does not correspond to reality. I'm not arguing C# is a good language for IoT. I'd personally use Rust for that in almost every situation, but the amount of false claims in this discussion is disheartening.

replies(1): >>41847131 #
10. neonsunset ◴[] No.41838658[source]
C# is the language with the best degree of control over allocation among all GC-based languages. You can write code that is completely allocation-free including abstractions. Most "data processing" APIs in the standard library do not allocate at all or allocate once for initializing e.g. lookup tables.

The actual issue is existing selection of runtimes for embedded platforms is limited: https://nanoframework.net/ and https://www.wildernesslabs.co/device (to be fair, a friend of mine uses the second one for automating his lab for his microfluidics devices research project, so it is useful).

replies(1): >>41840559 #
11. neonsunset ◴[] No.41838790[source]
This was recently discussed: https://news.ycombinator.com/item?id=41801124

While it is true that Rust is a strictly superior option for highly concurrent systems code, it still leaves areas where you can make a mistake regarding lock management and other advanced forms of synchronization.

In addition to that, .NET as platform is fairly tolerant to misuse and calling the code that is not thread-safe from multiple threads concurrently usually leads to logic bugs or "stop modifying this collection concurrently, please" exceptions but not to catastrophic memory safety issues like it happens in C/C++.

You can read more on its low-level memory model here: https://github.com/dotnet/runtime/blob/main/docs/design/spec...

> The same is true for async, which in C# is also a problem.

Now, this one is strictly not true. Async primitives are thread-safe. In Rust, you must synchronize because at the very least you must deterministically deallocate memory used by shared state between the tasks. In C#, this complexity is handled for you by a GC (ironically, you get negative sentiment towards async from people having experienced Python's async or Rust's async complexity, assuming the same applies to C#). In some scenarios, it is also a throughput optimization since it reduces memory contention by not modifying the cachelines shared between the cores, lending itself into better performance on many-core systems - the memory is modified/reclaimed when it's no longer in use, while the actively shared data is placed elsewhere.

12. fwip ◴[] No.41839000[source]
I think the thing you're missing here is that "Internet of Things" usually means machines way beefier than "embedded."

IoT is roughly equivalent to a Raspberry Pi - the thing will usually have an operating system that you're running on top of, and most of your existing knowledge about computers will port over.

Embedded is the chip in a happy meal toy, or your microwave in 1995. There is no "operating system." Your code is the only code running on the machine.

replies(1): >>41842033 #
13. throwawaymaths ◴[] No.41839112[source]
Easy. Decide to use an ecs system because of its lower memory footprint. Pass around integer indices. Use two threads. Data race.
replies(1): >>41847247 #
14. 15155 ◴[] No.41840559{3}[source]
> C# is the language with the best degree of control over allocation among all GC-based languages.

It's the best, but if I can't say: "never heap allocate under any circumstances," it's a problem.

replies(1): >>41840747 #
15. neonsunset ◴[] No.41840747{4}[source]
You can :) It just requires going out of your way a bit. It effectively works as C with better type system and generics (C++ lite if you will), that can be either JIT or AOT compiled (statically linked too). For example https://github.com/bflattened/bflat has a target flavour that does not support GC at all.

I'm not saying it's an optimal choice for embedded, only pointing out that you can write allocation-free code with confidence. Patterns that allocate are known - predominantly boxing and closures. You can easily avoid both, you can also declare your structs as 'ref struct' which completely prohibits placing them on the heap, either as a box or as a part of heap-allocated memory inside some other object/struct.

16. buescher ◴[] No.41842033[source]
That kind of embedded work, like connect-the-dots pcb layout work, went overseas 15-20 years ago. The typical new embedded project in the US today has minimally a not-particularly-resource-constrained 8-bit processor with gobs of fancy peripherals, something like the PIC Q10 series or the ST equivalent. The median would be a 32-bit ARM Cortex M.

Typical IoT SoCs are a step up from that but it’s not huge. The ESP32 is pretty representative but there are significant other options - see what Amazon supports in FreeRTOS as a starter.

It’s a big leap from small IoT SoCs to SBCs like the Pi.

17. sn9 ◴[] No.41843390[source]
It's kinda like getting the benefits of F#'s type system but with compilation to native code without garbage collection or data races.
18. leoedin ◴[] No.41847131{3}[source]
Are you actually familiar with those projects, or are you just posting links?

Nano Framework is a framework to run C# apps on microcontrollers - but when you get to the hardware interfacing, it's done in C++. Look at https://github.com/nanoframework/nf-Community-Targets/tree/m... for an example. It appears to be using the ChibiOS RTOS at the low level.

Wilderness Labs have built a C# runtime on a microcontroller. But if you got access to their source code, I can guarantee that somewhere there's C++ or C code setting everything up, kicking off the virtual machine etc.

Nobody is claiming that you can't run bytecode-compiled scripting languages on embedded targets. Hell, you can even emulate other CPUs on embedded targets. But ultimately to set up a microcontroller and use its peripherals you need to be able to read and write registers on the memory bus, which means you need to be able to create assembly that the CPU understands.

There are projects to compile C# directly to machine code, bypassing the need for C++, C or Rust to set everything up. But those aren't at all mainstream. C# code expects a memory allocator and garbage collector. You'd have to explicitly run those somehow.

If you're already going to have to write some sort of board support package in a language that compiles directly to ARM machine code, then Rust is a great candidate. And if you're already writing your board support package in Rust, it might make sense to write your business logic in Rust too.

19. lionkor ◴[] No.41847247{3}[source]
That seems like a lot more effort than in C#