Hacker News new | past | comments | ask | show | jobs | submit login

I have some time between contracts and I've found myself learning rust.

Pros:

- Sum types. I am an OO apologist, but trying to use classes in C++ is an exercise in frustration. Sum types map very well to union types and are a better fit for systems programming.

- Unit tests built right into the language - it seems like a small thing but it's a hassle in most languages to choose a library, set it up etc.

- The above, combined with cargo-watch [0]. With the right flags, you hit save, and your tests all run. It's a god-send.

- Ecosystem is pretty big. I can find most things I need.

- Discord channel is nice. I was put off a bit by the Rust Evangelism Strikeforce back in the day, but so far everyone is pretty chill.

- Options & Results in the standard library. All languages should have this

Cons:

- I find it hard to transfer knowledge of C to rust, because they use different terminology.

- Docs can be confusing to read because every method on collections like `map` or `filter` returns its own Trait. People have explained why this is to me a couple of times but I still haven't gotten it through my head.

- You still have to think about memory and pointers. Not always a bad thing, but it is an extra dimension when solving a problem.

Overall I'm powering through. I'm hoping to get to a point where Rust is an obvious choice for anything that involves finer control of memory.

[0] https://github.com/watchexec/cargo-watch




As someone with 20 years of C++ experience, in industries from everything from real-time systems through to low-level HPC graphics on CPU/GPU (currently in VFX), I really don't understand the hatred for OO programming, and I must be either missing something, or being lucky with the codebases I'm working on...

It's a tool for encapsulation: you can get into trouble with it, but I'd argue the same is possible with many things - it's how well you use it. You can easily use state composition in C++ if you want, and generally (multiple inheritance can cause issues in some cases) you can use interface classes as well.

And as someone who's been learning Rust for the past 6+ months, I've found that of the three new projects I started which I started in Rust instead of C++/Python over the past 4 months, Composition actually worked quite less well (it's quite verbose due to all the plumbing needed to connect things up) than if I could have used OO interitance in many situations where I wanted specialisation - i.e. hierarchy behaviour traits that had both common shared state and functionality AND specialised state and functionality. Thankfully Traits can at least have default implementations - if not, things would have been a bit worse.

To be clear: there's a lot to like in Rust though.


Encapsulation is just another way of saying correctly used visibility modifiers. It doesn't mean the state has to be inseparable from the behavior, and its role is semantically fulfilled in Rust by modules. And IME issues with OO feeling like the better role tend to be people attempting to solve all the problems with one feature like OO lets you do, shying away from doing it with multiple features even though they're really multiple concerns. E.g. for common behavior manipulating common state among several local structs, a macro is what you'd use.


The 'hate' is reserved for implementation inheritance, which has its well-known drawbacks (research the "fragile base class problem") - basically, it introduces complex long-range coupling throughout inheritance hierarchies that makes it way too hard, or even impossible, to evolve code over time. Everything else is very much doable in Rust.


The criticism of OO is more Java and Gang of 4 related than anything.

Two things get the enterprise devs of the mid 2000s going:

Badmouthing Oracle. And complaining about refactoring someone's mess of an abstract factory.

Especially I would say since microkernels just became incredibly widespread After Docker. Encapsulation is just not as necessary as it used to be.

In C++ the whole encapsulated structs thingo works fine.

It's there for when you need it, and you don't need it when you don't.

Gang of 4-esque programming is okay when it's necessary. Sometimes you are making something with a 100 different buttons and you just gotta do it that way.

However, for every project that needs all those factories and such, there's probably 5-10 where straight procedural/functional programming is perfectly fine.

Or in the case of many data oriented applications, or when formal verification is needed, probably just straight better.

Just do your abstraction with subroutine signatures and scope.


One of the pieces of OO _inheritance_ I deeply miss in Rust is the ability to subclass a library class, override *one* function, and retain all the other functionality. Yesterday, I needed to tweak the behavior of one function on actix-identity's cookie and my option seemed to be to wrapper the original object and write the same interface for the entire object, passing through every function except the one I wanted to override. Am I a sinner for wanting inheritance yesterday? Probably, but I had to write so much boilerplate to achieve my tiny override.


You don't necessarily need inheritance. Kotlin for example has a feature wherein all that forwarding boilerplate you had to write is automated.

https://kotlinlang.org/docs/delegation.html


That sounds fantastic. It also sounds a bit like a vtable :-D


I guess one way would be via delegation, similar to how COM does it.

I bet with a couple of macros it would be possible to automate what languages on Windows can somehow do magically with COM interfaces.


You don't need any macros, it can be accomplished with the trait system[1], either manually or through auto-deref. Some of the boilerplate could be generalized with a derive proc-macro or a macro-by-example call but it is not absolutely necessary.

[1]: https://play.rust-lang.org/?version=stable&mode=debug&editio...


Thank you so much for this.


> As someone with 20 years of C++ experience... I really don't understand the hatred for OO programming

Well, where should I begin:

    std::cout << "Hello world";
Is just a nice way of doing things...

As the other commenter said, it's not so much OO, but more GoF and how the 2000's OO went.

Please tell me why any basic library adds 3 or 4 levels of inheritance just to do something basic like vector or array. "Oh but this is to make things more generic" and yet nobody uses all that generic stuff. STL is often replaced by more "serious" users.

C++ (and Java) are 2nd systems syndrome gone to 11

I've just taken a look at the Vector implementation at my current system and it's an exercise in frustration. Meanwhile I can understand most of this https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#400


What does std::cout << has anything to do with OOP? It is simply operator overloading.

Where is the level of indirection in std::vector implementations? With templates, c++ really doesn’t use virtual dispatch all that much.


> has anything to do with OOP? It is simply operator overloading

It is an example of how they fumbled the implementation of APIs, because this one has very poor usability.

> With templates, c++ really doesn’t use virtual dispatch all that much.

Good, but try to understand why your multiple-inherited vector is not compiling by reading a 3 line error message.


I wasn't criticising OO. I was talking about how much of a square peg in a round hole it felt - for me - to do OO in C++.


> every method on collections like `map` or `filter`

I think you're remembering the various iterator adaptors, but they aren't methods on collections, they work on Iterators as their name implies. Somebody else explained why these methods can't all just return "Iterator" (that isn't a type) but just thought I'd mention you won't find map and filter in Rust's Vec or HashMap types, those methods live in the Iterator trait.


`iterator.map(…)` must return a new wrapper type, because it needs to store the closure somewhere, so it returns

    struct Map { closure, original_iterator }
This fact could have been hidden by making these methods return `impl Iterator`, but it would be strictly less useful/performant in case you needed to store the iterator somewhere, because then you couldn't name the actual type, and would need to work with an abstract one.

But I do agree that the way documentation presents traits and their implementations can be overwhelming, especially the Iterator which is a pretty large trait.


> because then you couldn't name the actual type

I'm sure you know this, but `type Alias = impl Trait;` is on its way which would make naming those possible.


Although that's true, and potentially useful as a feature (not sure I want it, but pretty sure I wouldn't be angry if it was added)..

I don't think that's what your parent was talking about when they say "couldn't name the actual type".

They're saying, suppose I have a function that says it return an Iterator and I realise ah, it's not really returning an Iterator, but some subtype of Iterator - or, in Rust where subtypes don't exist, a type which merely implements Iterator. I now can't talk about the actual type being returned, because that's hidden from me, and it needn't be.


For posterity, this is Rust RFC 2515 type_alias_impl_trait.


They return their own type, all of which implement a common trait, because traits are not types, and there's two options for representing 'any type that implements this trait', one of which can't be returned without allocating it (dyn), and the other of which can't be used in trait methods (impl). As a (frankly minimal in my opinion) effort to make this less weird to look at, the docs now have a (nearly invisible) 'notable traits' hoverable, which tells you what types implement Iterator and that anything other than them implementing Iterator is not very important.


Re: knowledge transfer. Why do you expect your C knowledge to transfer to Rust? Let me tell you clearly: it doesn't. It's not because Rust uses different terminology. Rather, Rust uses different terminology because underlying concept is different, so using same terminology would only lead to confusion.

I found this to be the case: Rust is equally hard to learn, for C programmers and for Java programmers. But C programmers get frustrated more, because while Java programmers don't expect their knowledge to transfer, C programmers have unrealistic and unjustified expectation of knowledge transfer. Actual learning curve is same, just expectations differ.

I wrote about this in 2016: https://sanxiyn.blogspot.com/2016/06/problem-in-rust-adoptio....


I expected (or should I say hoped) C knowledge would transfer because - that's my main point of reference for a programming language where you care about memory.

I don't think there's anything particularly unusual about it - were I to seriously learn haskell, I'd hope whatever knowledge I had of Ocaml would put me in a good position.

Your blog was spot on - with the caveat that people who know C++ can easily "learn" stuff like Java and C# but tend to write very C++ flavoured Java and C#.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: