Live data from Hacker News

How I went about learning Rust

eli.thegreenplace.net

211–220 of 303 posts

Re: How I went about learning Rust

#211

Earlier quoted context omitted.

Read about sum types. They exist in Haskell, Rust, OCaml, Typescript, Swift, Kotlin, etc. You are likely only familiar with product types without knowing they're called product types. (Cartesian product) You can have 100% type-safe, guaranteed at compile time code without null that can still represent the absence of data. Once you've used sum types, you feel clumsy when using Javascript, Python, Go, Ruby, C, C++, etc…

Arguably dynamic languages have sum types: every variable is one big sum type with the variants being every other type! I suspect the lack of sum types in many static languages are partially responsible for the popularity of dynamic ones.

> I suspect the lack of sum types in many static languages are partially responsible for the popularity of dynamic ones.

I can totally see this. I started writing a small cli tool in Go, and despite knowing way less Rust, I switched to it and was able to make a lot better progress at first due to pattern matching and result types. It was just so much easier/more ergonomic to write a simple parser.

The Go code was a mishmash of ugly structs and tons of null checking and special casing.

Re: How I went about learning Rust

#212
post #206

Earlier quoted context omitted.

Yes I tried all those 'minimize rust' approaches including the above one. they worked fine if you're statically link to its stdlib. but if you have a few complex rust binaries, static link for each of them is not going to help on the overall combined size. I did use a released library and build everything for release(per those minimize projects), I can easily cut a small program from 3M to 290KB but again, it is eith…

Oh so your problem is that when attempting to dynamically link to the standard library, you're missing out on the dead code elimination you'd get when statically linking. Rust doesn't have a stable ABI anyways, so you can't really share the standard library between programs unless you're really careful.

you nailed it.

neither LTO nor panic=abort can be used at dynamic link, no stable ABI is another thing.

to me it means Rust is not a good fit for embedded space for majority of the use cases, sadly.

Re: How I went about learning Rust

#213
post #206

Earlier quoted context omitted.

Oh so your problem is that when attempting to dynamically link to the standard library, you're missing out on the dead code elimination you'd get when statically linking. Rust doesn't have a stable ABI anyways, so you can't really share the standard library between programs unless you're really careful.

you nailed it. neither LTO nor panic=abort can be used at dynamic link, no stable ABI is another thing. to me it means Rust is not a good fit for embedded space for majority of the use cases, sadly.

I'm not sure what embedded space you're referring to, but microcontrollers, in my experience, generally use monolithic binaries. I'm unsure why you'd wish to use dynamic linking in those cases.

Re: How I went about learning Rust

#214

Earlier quoted context omitted.

Having studied mostly OOP/Java at the uni, and been using mostly C# at work, I realized I might actually start to find programming fun again through Go. I've studied it a bit recently and this idea of interfaces with composition over inheritance etc. made somehow a lot more sense to me, and gave me this boost to try and learn it more because it felt so enjoyable. Not even with some practical problem at hand to solve,…

Go is fun at first, and then it becomes soul sucking. It's all boiler plate. Many large scale projects have a lack of adequate unit testing, so large code bases are particularly painful to maintain. I attribute this lack of tests due to how the code needs to be structured, you have to needlessly add 'interfaces' throughout your code to accomplish things. Go has it's strengths, but IMO fun isn't one of them. It turns…

You must learn to love the boiler plate, understand why doing the boiler plate well matters.

Re: How I went about learning Rust

#215

Earlier quoted context omitted.

> Rust actually supports most OOP features, with the main exception of implementation inheritance. I've tried some basic OOP. Fields from the base class need to be redefined in each child, and making shared methods private require an ugly workaround. Both concepts are very basic OOP concepts, not advanced or inherently/tendentially dangerous, and the results is that even basic Rust OOP requires a lot of boilerplate.…

You can define a field with the type of the base class in the subclass, and impl Deref for SubClass, with Target = BaseClass.

You can, but the docs for Deref beg you not to use Deref for types that aren’t smart pointers because of how confusing and surprising the results can be to an unfamiliar reader of the code.

Re: How I went about learning Rust

#216

Earlier quoted context omitted.

> but there's no job in That's just not true. Rust got already adopted by lot of either big or interesting to work at players (Amazon, Microsoft, DropBox, ...?) and, while anecdotal, I myself get also paid to program rust. > the community is toxic. > In the rust community, just like a sect, everybody must say that everything is just perfect. I often get the opposite feeling with all the diverse and lengthy discussion…

> Rust got already adopted by lot of either big or interesting to work at players (Amazon, Microsoft, DropBox, ...?) and, while anecdotal, I myself get also paid to program rust. There are very few open positions, though. Just check out the Rust newsletter - the open positions for each release can be counted on one hand. I have the suspicions that Rust positions are typically filled in-house. Shopify, for example, ad…

Our company (Prisma) is currently hiring Rust engineers. Remote jobs, office in Berlin for those who need it and interesting problems to solve.

Re: How I went about learning Rust

#218
post #200

Earlier quoted context omitted.

>If it's a single score, I'd still want to use null / int. In your example, you still have to manually check if there's a value every time, but this is not compiler-enforced. Should you forget, you will get a runtime crash at some point (likely in production at a critical time) with some kind of arithmetic error. This wouldn't be possible with a simple sum type. Also, a sum type with units of Score(Int) and NoScore w…

The compiler would enforce Number-ness every time I try and run a function that takes a number, right? Wouldn’t I still have to check for NoScore? > a sum type with units of Score(Int) and NoScore won't allow assignments of any other "null" instances. I get this part - I wouldn’t be able to assign ‘NewBornBaby’ (my name null) to ‘NoScore’ (my score null)

> The compiler would enforce Number-ness every time I try and run a function that takes a number, right?

> Wouldn’t I still have to check for NoScore?

No, because you differentiate between the sum type (e.g. Maybe in Haskell) and the number type at compile time. It's a small distinction - there will still be one or two places where you ask "is this a Maybe-score, or a Just-Score, or a No-Score", but the upside is that in all places you are very clear if a No-Score is possible, and you can't confuse the value with the error signal.

I.e. if you pass maybe-scores to something that computes the mean, you'll get a compiler error. The writer of the mean function doesn't need to care you've overloaded an error value onto your numbers.

The compiler support is the important part. Languages like C/C++ know sum-types just fine. They usually surface as sentinel values (NULL for ptrs, -1 for ints, etc) or unions with type fields. The stdlib offers it as std::optional. As you progress along that spectrum, you get increasing compiler support there, as well.

One could even argue that sentinel values are a slightly better choice than Go's pairs, because they are closer to being sum-types than the strict product type that is go's (result, error) tuple - at least sentinels can't be simultaneously carrying a valid value and an error.

Re: How I went about learning Rust

#219

Earlier quoted context omitted.

Rust actually supports most OOP features, with the main exception of implementation inheritance. And implementation inheritance is a nasty footgun in large-scale software systems (search around for: "fragile base class problem"), in a way that just doesn't apply to simple composition and pure interfaces (traits). So it's hard to fault Rust for including the latter and not the former.

For me, one of the main reasons to use OOP is dynamic dispatch / runtime polymorphism. I have not spent much time with Rust, but it seems like that is a bit cumbersome there, and doesn't exactly work like you would expect from Java, C++, Python, .... I mostly use OOP when I have a bunch of Foos and Bars, and want to treat them differently in some places. Instead of having ifs in each function, I use inheritance, and…

What you describe can be achieved in Java via composition (instead of inheritance) and using interfaces.

Re: How I went about learning Rust

#220
post #181
post #158

Earlier quoted context omitted.

I'd consider something OOP if it has the same expressive power i.e. things expressible in OOP languages are easily expressible in another OOP language as well. And for Rust that's not the case. Otherwise you end up with Haskell is an OOP language.

I will debate it is one, yes. Just like being an FP language isn't "like Haskell does it", when I learned FP, Haskell didn't even exist. Try to express Eiffel, CLOS, SELF or BETA in Java. By the way, here is One Weekend Raytracing in Rust, perfectly using OOP with dynamic dispatch and interfaces (sorry traits). https://github.com/pjmlp/RaytracingWeekend-Rust

Ok. But noticed what I said. *Things* that are easy in one OOP should be similarily easy to express in other OOP language.

Counter example: Html5 DOM.

Post reply on HN