That guy saying that you can easily avoid GC in C# is also not realistic or doesn't know what he's talking about - even language expressions can lead to object allocation in C# - you really need to know what you are doing to avoid the landmines - it's very clear the language wasn't designed for this - if you need a lot of code like that.
Regarding C#, I said for the most part. I stick by my assertion that (a) GC is quite efficient (esp.w/gen0 objects generated by your "expressions") and far from "lame" and (b) working with value types and Span<> (like Rust Slices) together can significantly reduce GC pressure to the point where it's acceptable, assuming there was actually an issue to begin with. Doing this in C# on hot-paths is certainly much less effort than moving to Rust wholesale, and you haven't thrown out the GC baby with the bath-water.
All the things that you do to make Rust efficient - allocate on the stack instead of the heap when you can, pre-allocate when size is known, use static/nested lifetimes, use slices to owned structures, etc., you can do in C#, but you don't have to worry about it until it actually becomes an issue.
If you're in the kernel or embedded system or the middle of a game rendering loop, then sure, Rust's compiler guarantees make this style programming easier if that's the way you want to go - and Rust has macros and other features that C# lacks. (Although the .Net JITTer is going to generate type-specialized methods and inline them, etc., w/a Rust macro you know up-front exactly what is being generated, which is nice.)
I've kept track of .NET progress since then and read about stuff like Span and ValueTask, ASP.NET Core team perf did a good job leveraging those for optimisations (eg. their optimised JSON parser) in such scenarios I agree C# with low level stuff sprinkled in is a good choice
But if your problem domain requires avoiding GC throughout and having better understanding on what the abstractions will compile to pick a language thats designed for that. It's like when I had to review some Java 6 code which tried to work around the fact that Java doesn't have value types with byte arrays - it was just soo bad compared to even C equivalent it was better to rewrite and go trough JNA.
But very little code actually looks like that in reality. A lot of code looks like that by accident.
Even in C++ I generally prefer to use handles over pointers for cases like that because graphs are just plain hard to get right, and if you use handles you can get a nice error message when something goes wrong instead of a segfault.
However, I usually pick Rust for performance-critical code with quite a bit of concurrency, so the problems involved are inherently tricky. I'm gaining more and more intuition about ownership and rustc-friendly software design, and so I hope I'll be able to better understand if my struggle is due more to limitations of the compiler and Rust's semantics or the limitations of my skills.
For comparison, I've written ~6000 SLOC of Rust in my lifetime, so I do have some experience but I'm definitely not an expert.
First having neither a build cache, or binary dependencies, means that C++ wins on the "make world" build, because naturally all my third party libraries are already compiled.
Then there is incremental compilation, incremental linking, pre-compiled headers and modules to help with the rest.
GUI code then becomes a fest of Rc<RefCell<>> in event handlers, or using arrays with vector clocks workaround as shown on Catherine's talk.
That sounds cool. An obvious question is, why not a lisp? But generally do you find f# works well on dotnet mixed with c#?
Go is interesting and I really wonder how it'd be doing if someone went all in on the multi-return syntax sugar (pretty awesome stuff), the prohibition (mostly) on exceptions, channels and trivial threading and a bunch of other nicely packaged features while giving some ground on Generics. All languages have their warts, but I think this one is particularly easy to address - though it may take a major version bump and introduce some BC breaks I feel like those could be minimized, the biggest cost would be library incompatibility.
Go doesn't do that though. There is some magic for builtins (e.g. variable-arity MRV) but as usual mere mortals need not apply.
C++ templates are a purely-functional Lisplike language. There's nothing strange or absurd about it; it's a very vanilla way to approach string rewriting systems.
(It's ugly to read, yes, but then all Lisplikes are too.)
My day job is ~half c++ and ~half c#, and I couldn't possibly disagree more.
The first problem is IDisposable and event handlers. Because c# doesn't have anything like weak pointers, event handlers require that you manually dispose of half your objects. In c, there's a simple rule: you always dispose of your objects. In c++, there's a simple rule: the destructor of your data structure/smart pointer always disposes of your objects. In c#, the rule isn't simple. Half the time, you have nontrivial destructor doing nontrivial things, the other half the time you can leave it up to the GC. But it isn't necessarily immediately clear which is which.
The second is `using`. Again, you must necessarily mix the semantics of non-deterministic GC cleanup and deterministic RAII cleanup. Which is worse than having a simple rule that always works.
https://docs.microsoft.com/en-us/dotnet/api/system.weakrefer...
https://docs.microsoft.com/en-us/dotnet/api/system.weakrefer...
https://docs.microsoft.com/en-us/dotnet/api/system.runtime.c...
> event handlers require that you manually dispose of half your objects.
You probably have software design issue on your day job. C# events can be great, but they're not a silver bullet.
For loosely coupled application wide events, event aggregator pattern works better, see IEventAggregator from Caliburn.Micro for an example.
If the two sides of the event handlers need strong coupling for a good reason, an interface or abstract class for the consumer works better, see ExpressionVisitor from System.Linq.Expressions for an example.
https://docs.microsoft.com/en-us/dotnet/api/system.runtime.i...
This and the the thread of the other day regarding having to use C for what C# is capable of, apparently many don't look on their toolboxes.
#[get("/hello/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
format!("Hello, {} year old named {}!", age, name)
}is more complex than express + Node. With added bonus that you get validation for free
require 'grape'
params do
requires :age, type: Integer
requires :name, type: String
end
get "/hello/:name/:age" do
"Hello, #{params[:age]} year old named #{params[:name]}!"
end
In any case, my beef is more with the macro than with the function annotation, which is rather spiffy. The macro hides the fact that string manipulation in Rust definitely requires a little bit of manual reading. And I feel that as soon as your app becomes more complex Rust will just start getting more in the way. And to an experienced Rust dev that might not be a big deal, because an experienced dev knows how to work with strings or which memory management strategy so it won't bog them down. But if you're just working on something and you quickly want to whip out a service that tells people what age they are, I'd definitely go for a quick Ruby or Go service. require 'sinatra'
get %r{/hello/([^/]+)/(\d+)} do |name, age|
"Hello, #{age} year old named #{name}!"
endThere's no cognitive load required to code in C++. You just don't know the language.
There's no easy tutorials or learning materials for it, but it's not hard.
> You just don't know the language.
Nobody knows the language haha. If anyone tells you they know C++, they're absolutely wrong. This has been, IME, the easiest way to know if a candidate is over-stating their resume -- if they say they "know" C++.
No.
> full of edge cases,
Definitely no.
> it blows my mind every time I pick it up.
I'm tempted to say "programming is not for you", but I'll be charitable and just point out that you've never learned the actual language to make statements like that.
> If anyone tells you they know C++, they're absolutely wrong.
That's a load of crapola. It's impossible to "know C++" in the sense of knowing the ISO standard for C++; but that is also true of any other language with a real standard.
Languages without standards, of course, are worse in every way.
False.
std::string x = y;
There's no way to mess this up.Again, learn the language. There is no such thing as "C/C++". 99% of the problems stem from the fact that people don't understand that C and C++ are completely different languages, despite sharing one compiler.
Learn C++ as itself and your problems go away.
> Lastly `const` what does it do where and why and should everyone ever use it...
`const` is a contract that means "I will not modify this object here". Nothing confusing or complex about it, unless you're trying to shoehorn this concept into C semantics somehow.
> No.
Most defenses I've seen of C++ boils down to something along the lines of "you're using it wrong (tm)".
I've seen enough C++ to take the side of "the design is probably wonky if it's that easy to do the wrong thing."
Maybe, but that wasn't what I said. The problem is that lots of people know C, but very, very few people know C++.
A big part of the problem is that both languages share one compiler, and people come from C thinking that C++ is just an upgrade with some features bolted on.
It's not. It's a completely different language, and if you approach it as "I'll learn a bit of C and then throw in some C++ features" you're setting yourself up for a world of hurt.
The hurt goes away if you forget C, start with a clean slate and learn C++ as a new language.
As for moving from C# to Rust, you can learn to program with the Span<> APIs and get the non-GC perf. of Rust for the most part and still keep GC and other C# niceties (like the whole toolchain and ecosystem...) in much less time than a wholesale move to Rust. As as for message-passing/agent/channel programming, you can certainly do that in C# if you like, and though there is certainly a lot a foot-shooting that can be done with async and thread contexts, in general the Task<> system is a joy to use compare to almost anything else out there, not only for async code, but for concurrent code.
Don't get me wrong, I always thought that C++ was a "worst of all worlds" language and appreciate moving to Rust from there, but for most user-facing applications that tend to have complex object lifetimes, I just can't see why you'd want to deal with RIAA when modern GC's are so darn good.