While I'm not sure what group ran the survey, the person who posted this on hn (steveklabnik) is a major reason for the success of Rust's community, which is likely a significant contribution to Rust's success as a whole.
> It does seem that having C++ knowledge helps with 20.2% of respondents with at least some C++ experience noting lifetimes to be “very difficult” while 22.2% of those without C++ knowledge found the topic to be “very difficult”. Overall, systems programming knowledge (defined as at least some experience in C and C++), tends to make for more confident Rust users: those with systems programming experience rated themselves as 5.5 out of 10 on their Rust expertise, while those with experience in statically typed garbage collected languages like Java or C# rated themselves as 4.9 out of 10. Those with only experience in dynamically typed languages like Ruby or JavaScript rated themselves as 4.8 out of 10.
Was this just based on self-report? An alternative interpretation could be that C/C++ programmers are just more confident.
It is based on self-report. I don't think that this statement is an interpretation exactly, it's simply saying that there's a correlation between answers on the survey. You are absolutely right that this may be the underlying cause.
I'm curious to know what people find to be missing in the Rust/C interop? I only used it for a few simple functions, so I could not hit any hard points.
Seeing how Zig has reimplemented the C preprocessor just to make C interop easier/smoother, there's always room for improvement beyond plain old function importing/exporting.
I of course, think that C FFI is as good as I personally need it to be. It's as good or better than most other languages that are not C.
Zig doesn't just implement the C preprocessor, instead the Zig compiler is also a C compiler, there are no separate compiler toolchains or build systems required for mixed Zig+C projects and you can directly import C headers into Zig source files.
I think C++ interop is the bigger deal than plain C. You end up needing to launder everything through C because of the lack of standard C++ ABI, plus there's a bunch of ergonomic awkwardness because C++ and Rust have a bunch of standard-library primitives that serve the same purpose but can't by passed by reference directly (vectors, slices, strings, tuples, etc.), and need to be manually marshaled back and forth if you have a Rust-flavored thing on one side but want to use a C++ library that expects C++-flavored things on the other.
I think at least for non-mutable stuff, C++ string_view (approximately rust &str), span (approximately rust slices), and the like should make at least some operations as simple as pointer casts, but having tooling to automate the boilerplate would be nice, and they're also new/not that widely supported (std::span is from C++ 20). And mutable stuff, if you want to be able to write idiomatic/ergonomic code on both sides, seems like a much bigger lift.
But this is the same story with pretty much every language, right? I remember dealing with some of the C++ interop layers in Common Lisp back about 10 years ago. All around an awful experience, and not really because of either CL or the interop layers themselves. It's just a boatload of complexity and the bigger the C++ library you want to target, the more likely it becomes that you have to deal with essentially all of the language.
Compare that to C, where the story is often as good as "import the header and you're done".
I'm not sure if there's a good path to a long-term solution, but I've been pushing to get nice C APIs in all the libraries I'm invested in, specifically because of this issue.
On Windows that solution is COM, which many consider dead, while it actually won the .NET vs COM wars during Longhorn, and was eventually improved with .NET type system (WinRT).
So while .NET type type doesn't cover all of C++, it does cover enough not to have clunky C interfaces, and nowadays many Windows APIs only have such COM/UWP interfaces as public API.
Microsoft is in the process of adding Rust to their COM/WinRT infrastructure.
One of the beauties of COM is that it is interface based component model, so it goes quite well with languages that have interfaces/traits as feature.
Calling simple and idiomatic procedural C interfaces [0] "clunky" and then contrasting them with the abomination that is COM is worthy of disagreement. Maybe the latter has its uses (I've never suffered enough through it to actually use it) but it definitely wins the clunkyness award. And that starts with its ridiculously unsearchable name.
[0] I really don't care if in C or a different language, but I'm not aware of relevant programming subcultures where that simplicity of interfacing is such an important pillar to their culture and their success.
When I attack C's clunky and archaic coding, I do so with full knowledge of C17 and how little it has changed with K&R C that I learned in 1992, in API design and OS security.
COM is a solution to tunnel C++ interfaces through C APIs though, and it ceates a lof of complexity to support OOP APIs which should have been plain procedural APIs to begin with (compare the mess that is WinRT with the traditional Win32 windowing functions).
Coincidentally, as an anecdotal data point, COM via C boilerplate (out of necessity) is all I've ever seen. And while it made me bloody angry each single time, I'm happy I didn't have to deal with the dogpile that was built on top of it.
There is, but it doesn't go as far as something like SWIG for example. You can't point it at a trait and say "make a C++ class for that" or vice versa.
Even C++ doesn't have proper interoperability with C++ ;)
IMHO going through C APIs is fine, because C++ interoperability is much harder and a moving target, just because the language surface is so much bigger than plain C.
It should be possible to compile C code in a Rust project without an external C compiler, or it should be possible to import a C header into a Rust source file directly (so basically, creating separate "Rust bindings" for a C library shouldn't be required).
See Zig as an example for excellent C interoperability.
As soon as a C/C++ compiler toolchain and build tools need to be involved in a Rust project you're basically loosing all advantages of Cargo and are back to the relative brittleness of cross-platform C/C++ builds.
Essentially people would like to be able to move seamlessly between idiomatic Rust and C (or even C++) without needing any manual intervention. This also would ideally include having an automatically safe API but this may require some glue on both sides of the API boundary.
For me I’m currently playing with async and finding myself needing nightly. I suspect a need for that will drop in the community once GATs land so it’ll be interesting to see how the nightly usage changes.
I’m also curious if the % using nightly number is getting skewed by the population growth of Rust itself rather than language feature changes itself (assuming the study is actually sensitive enough to confidently state a 2% change is outside of the error margin).
bound_cloned, option_result_contains, str_split_once are minor convenience shortcuts for things you can do slightly more verbosely on stable.
The other half are all about const generics and const fns. You can emulate some of them on stable, but the cost far outweighs that of switching to nightly in my case.
Ha, fair enough. But hey, this is a personal side project and I don't mind kicking the tires of a couple upcoming features before they get stabilized. Gotta help out oli-obk on his quest to const all the things.
If you want to actively observe me writing code by giving me a channel to DM you you can get a sense of my questions and how a historically majority C++ developer since my teenage years is transitioning to this language. I feel like this is a large constituency so figuring out my mental model errors is important to help write detailed transition doc material.
Please do note down the questions and publish them later. Steve may or may not have the time since he has a full time job not related to the Rust project, but I’m sure the community would love to read about your experiences.
Thanks! I won't have the time to do that, but anything you want to send me, I'm happy to check out. Your other answer is already very comprehensive and useful. I appreciate it.
I haven’t tried GATs. I’m using async_trait crate to poly fill needing that. I can’t recall offhand the unstable features I’m actually using. But generally I think async + traits don’t play well together currently (like you can get it to work but there’s a lot of surprising sharp edges and certain things that aren’t even possible yet). I’m also fully aware that I may not be writing “Rusty” code as I’m mainly coming from a C++ background and exploring Rust on my own.
Generally I’d say the async stuff is still pretty confusing. Like not if you’re entrenched in the Rust ecosystem but it seems like you have to choose the runtime you build around (Tokio vs async_std) and they’re drastically different maturity levels and feature sets so it’s more complex then just picking one as now you have to make sure the entire dependency train is consistent (or you end up with multiple competing implementations running within a system) which I’m concerned about as you scale up the complexity of a codebase (and writing code that doesn’t have to care is similarly difficult it seems where you kind of have to choose a specific dependency to get access to even basic I/O).
The other rough edge is that certain lifetime stuff in Rust still is over specified I’d consider. The example is unpacking a struct to be parameters for a function. Like
do_something(&mut self.x, &self.y)
complains that self ownership is being taken in conflicting ways and you have to add two dummy variables instead. It’s definitely better when I played with Rust a couple of years ago and it feels close but it still feels like those rough edges are an unnecessary barrier for beginners.
The other one is it feels like the compiler has reduced in power from suggesting what modification needs to be made. Either:
1) I’m writing more complex/different code than before. Possible since I’m doing an async codebase from scratch vs contributing to a more established Rust codebase.
2) most of the corrections it made before have already been incorporated in the automatic stuff the compiler is now inferring meaning the remaining pieces are the hard ones that have never had corrections in the first place.
3) the focus on auto-correction suggestion has diminished so new features are going in without considering as well as before how to guide the user to fix the issue (or these are harder suggestions in this space that haven’t been explored yet as the original suggestions were easier/better studied). The focus switch could even be intentional (other areas of the language need more focus)
4. I’m sure there’s other possibilities.
The final nit I’d note is one around crate discoverability. There’s a lot of crates out there and there’s a lack of documentation on them (ie are they abandoned, “finished”, active development) and selection criteria (ie which crate should I use given my project dependency chain, perhaps crowdsourced ML suggestions from other projects like a “Netflix” recommendation with ability to inject additional details like “I’m looking for something that provides certain algorithmic or memory guarantees, is async or not, etc” if those things aren’t available from the existing dependency but a new thing being added). The data is there nominally as fancy charts because that’s kind of easy but the harder proactive advice with explanations is the real gold no one has done and can really push Rust to the next level for engineering efficiency.
It's a small feature, but I'm really looking forward to seeing this stabilized so I can get off of nightly and still have a break before else statements (aka "ClosingNextLine"): https://github.com/rust-lang/rustfmt/issues/3377