6 ms·
One example where Rust enables better and faster abstractions is traits. C you can do this with some ugly methods like macros and such but in Rust it’s not the
by bfrog 9mo ago
One example where Rust enables better and faster abstractions is traits. C you can do this with some ugly methods like macros and such but in Rust it’s not the implementers choice it’s the callers choice whether to use dynamic dispatch (function pointer table in C) or static dispatch (direct function calls!)
In c the caller isn’t choosing typically. The author of some library or api decides this for you.
This turns out to be fairly significant in something like an embedded context where function pointers kill icache and rob cycles jumping through hoops. Say you want to bit bang a bus protocol using GPIO, in C with function pointers this adds maybe non trivial overhead and your abstraction is no longer (never was) free. Traits let the caller decide to monomorphize that code and get effectively register reads and writes inlined while still having an abstract interface to GPIO. This is excellent!
- K0nserv 9mo ago> In c the callers isn’t choosing typically. The author of some library or api decides this for you. Tbf this applies to Rust too. If the author writes fn foo(bar: Box<dyn BarTrait>) they have forced the caller into dynamic dispatch. Had they written fn foo(bar: impl BarTrait) the choice would've remained open to the caller
- nicoburns 9mo agoRight, but almost all APIs in Rust use something like fn foo(bar: impl BarTrait) and AFAIK it isn't possible to write that in C (though C++ does allow this kind of thing).
- bfrog 9mo agoC++ you either use templates or classes and virtuals. In either case the caller doesn't get to decide.
- Seattle3503 9mo agoInteresting, there isn't some way to have a template that is polymorphic over virtuals?
- Maxatar 9mo agoIn C++ you do it the other way around, have a single class that is polymorphic over templates. The name of this technique within C++ is type-erasure (that term means something else outside of C++). Examples of type erasure in C++ are classes like std::function and std::any, and normally you need to implement the type erasure manually, but there are some library that can automate it to a degree, such as [1], but it's fairly clumsy. [1] https://www.boost.org/doc/libs/latest/doc/html/boost_typeerasure.html https://www.boost.org/doc/libs/latest/doc/html/boost_typeera...
- bfrog 9mo agoIt's neat this is a thing I guess, but I agree it looks fairly clumsy compared to the Rust answer.
- bsaul 9mo agohow do apis typically manage to actually « use » the « bar » of your example, such as storing it somewhere, without enforcing some kind of constraints ?
- steveklabnik 9mo ago"BarTrait" is the constraint. This is monomorphized for every type you pass in, in short.
- Maxatar 9mo agoIf you need to store the value then you have no choice but to take in a dyn trait.
- steveklabnik 9mo agoDepending on exactly what you mean, this isn't correct. This syntax is the same as <T: BarTrait>, and you can store that T in any other generic struct that's parametrized by BarTrait, for example.
- marcosdumay 9mo ago> you can store that T in any other generic struct that's parametrized by BarTrait, for example Not really. You can store it on any struct that specializes to the same type of the value you received. If you get a pre-built struct from somewhere and try to store it there, your code won't compile.
- steveklabnik 9mo agoCan you show me what you’re talking about? I don’t understand what you mean. I’ll add a code example of what I mean in a bit.
- steveklabnik 9mo agoHere's what I'm talking about: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=257b78a2c837c0217da5b8d1cd993f93 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- embedding-shape 9mo agoIt's a tradeoff though, as I think traits makes the Rust build times grow really quickly. I don't know the exact characteristics of it, also I think they speed it up compared to how it used to be, but I do remember that you'll get noticeable build slowdowns the more you use traits, especially "complicated" ones.
- treyd 9mo agoCode is typically run many more times than it's compiled, so this is a perfectly good tradeoff to make.
- cardanome 9mo agoFor release builds yes. For debug builds slow compile times kill productivity.
- greener_grass 9mo agoIf you are not willing to make this trade then how much of a priority was run-time performance, really?
- deleted 9mo ago[deleted]
- esrauch 9mo agoIt's never the case that only one thing is important. In the extreme, you surely wouldn't accept a 1 day or even 1 week build time for example? It seems like that could be possible and not hypothetical for a 1 week build since a system could fuzz over candidate compilation, and run load tests and do PGO and deliver something better. But even if runtime performance was so important that you had such a system, it's obvious you wouldn't ever have developer cycles that take a week to compile. Build time also even does matter for release: if you have a critical bug in production and need to ship the fix, a 1 hour build time can still lose you a lot here. Release build time doesn't matter until it does.
- emidln 9mo agoI probably enjoy ELF hacking more than most, but patching an ELF binary via LD_PRELOAD, linker hacks, or even manual or assisted relinking tricks are just tools in the bag of performant C/C++ (and probably Rust too, but I don't get paid to make that fast). If you care about perf and for whatever reason are using someone else's code, you should be intimately familiar with your linker, binary format, ABI, and OS in addition to your hardware. It's all bytes in the end, and these abstractions are pliable with standard tooling. I'd usually rather have a nice language-level interface for customizing implementation, but ELF and Linux scripting is typically good enough. Binary patching is in a much easier to use place these days with good free tooling and plenty of (admittedly exploit-oriented) tutorials to extrapolate from as examples.
- pmarin 9mo agoThe C way is to avoid abstractions in first place.