Live data from Hacker News

The Rust borrow checker from a different perspective

blog.systems.ethz.ch

21–30 of 51 posts

Re: The Rust borrow checker from a different perspective

#21
post #10

From that writeup, it seems like one is forced to program Rust in an object oriented manner. Is it possible to program it in a functional manner like Lisp, or a procedural manner like BASIC or assembler?

> From that writeup, it seems like one is forced to program Rust in an object oriented manner.

That's really not the case. In fact I'd say the opposite, the focus on ownership, limitations of the borrow checker (partial borrows through function calls) and lack of inheritance makes "object oriented programming" rather difficult. That functions are commonly namespaced through traits or structs does not intrinsically make it "object oriented".

It has a number of functional APIs (many standard structures have various HoFs for manipulating them and combining them), although the ownership and borrow checking bits can also make them somewhat less convenient.

Re: The Rust borrow checker from a different perspective

#22
post #14
post #10

From that writeup, it seems like one is forced to program Rust in an object oriented manner. Is it possible to program it in a functional manner like Lisp, or a procedural manner like BASIC or assembler?

In my experience, it’s not much more OO than Haskell. Even in many functional languages, you typically want some OO — you’ll want some core type that you export opaquely, and then you have functions doing logical operations on that. I think two of the biggest differences are: - Because the type and memory systems make it possible to skip heap allocation if you’re careful, it’s “idiomatic” to prefer avoiding technique…

Regarding closures, note that Rust closures don't allocate on the heap unless you explicitly use a Box to put them on the heap. Long-lived closures aren't a problem, because closures by default borrow the variables they close over (and like all borrowed references, these cannot extend the overall lifetime of the borrowed data); alternatively they may take ownership of their closed-over variables, but in that case the closure can only be called exactly once (as expected for owned values).

Re: The Rust borrow checker from a different perspective

#23
post #12

Earlier quoted context omitted.

Just to be clear, rust is not object oriented. You do not use inheritance nor do you have objects. There was a recent post comparing rust to haskell https://www.fpcomplete.com/blog/2018/10/is-rust-functional . And tldr is yes it lends itself to a functional style...

> And tldr is yes it lends itself to a functional style Rust does a good job of supporting a functional style efficiently. It's also considered idiomatic to code functionally. Probably the biggest, obvious exception is that Rust embraces mutability because it's statically safe to do so i.e. you aren't as susceptible to the traditional disadvantages of mutability. > You do not use inheritance nor do you have objects T…

> That's only partially true though. Rust does support interface inheritance and polymorphism in its trait system.

It supports no more than Haskell's typeclasses (rather less in fact), are typeclasses an object system?

> And enums are a closed form of sub-typing.

Rust enums are pretty standard sum types, a staple of statically typed functional languages.

> RAII is also idiomatic and I'd argue that many would class that as object-oriented.

That's defensible but setting one hell of a low bar on the concept of object orientation.

Re: The Rust borrow checker from a different perspective

#24
post #15
post #10

From that writeup, it seems like one is forced to program Rust in an object oriented manner. Is it possible to program it in a functional manner like Lisp, or a procedural manner like BASIC or assembler?

I wouldn't say that object oriented is completely opposed to functional, though.

Indeed, Scala's main innovation was to demonstrate that functional programming is a well-behaved edge-case of OO programming.

(Before Scala, research was mostly trying to show the opposite: how OO is an edge case of FP, but that didn't work quite so well.)

Re: The Rust borrow checker from a different perspective

#25

Earlier quoted context omitted.

> And tldr is yes it lends itself to a functional style Rust does a good job of supporting a functional style efficiently. It's also considered idiomatic to code functionally. Probably the biggest, obvious exception is that Rust embraces mutability because it's statically safe to do so i.e. you aren't as susceptible to the traditional disadvantages of mutability. > You do not use inheritance nor do you have objects T…

> That's only partially true though. Rust does support interface inheritance and polymorphism in its trait system. It supports no more than Haskell's typeclasses (rather less in fact), are typeclasses an object system? > And enums are a closed form of sub-typing. Rust enums are pretty standard sum types, a staple of statically typed functional languages. > RAII is also idiomatic and I'd argue that many would class th…

> are typeclasses an object system

No, did someone say they were?

> Rust enums are pretty standard sum types, a staple of statically typed functional languages

Indeed

I'm not entirely sure what your point is. Functional and OO aren't mutually exclusive.

Re: The Rust borrow checker from a different perspective

#26
post #7
post #6

Earlier quoted context omitted.

Can someone chime in as to why C++ compilers in particular are notorious for extremely verbose yet unhelpful error messages? Is it something endemic to parsing the language that there can't be errors as helpful as Rust's?

The short answer, devoid of some interesting nuance, is that C++’s template system is similar to an untyped functional programming language, so the errors you get are the compiler’s attempt to make sense of the large tree of nonsense it couldn’t reduce. C++ also suffers from years of backwards-compatibility twister creating abundant cases of ambiguity if you do something wrong. The compiler will do it’s best, but if…

> Rust is far simpler in that regard, so there are fewer instances where your code might accidentally make sense to the compiler if you have an error.

I say this from a place of love for Rust, but the first time you run into a massive generic type tree error thats root cause is a type parameter three levels deep not implementing Send (or some other trait) it takes your breath away.

There are some great jokes about this in Deisel and Futures for example. It makes Java generics look like a baby just starting to craw.

The error messages are good, you just need to go grab a coffee while you read through the book on type theory that’s been dumped to your screen.

Re: The Rust borrow checker from a different perspective

#27
post #7
post #6

Earlier quoted context omitted.

Can someone chime in as to why C++ compilers in particular are notorious for extremely verbose yet unhelpful error messages? Is it something endemic to parsing the language that there can't be errors as helpful as Rust's?

The short answer, devoid of some interesting nuance, is that C++’s template system is similar to an untyped functional programming language, so the errors you get are the compiler’s attempt to make sense of the large tree of nonsense it couldn’t reduce. C++ also suffers from years of backwards-compatibility twister creating abundant cases of ambiguity if you do something wrong. The compiler will do it’s best, but if…

Let me just stress the first point, because that's exactly where Rust and C++ differ: C++ templates really are untyped. There is no way to check whether a template definition is correct. A C++ compiler has to first expand the templates ("complete monomorphisation") and then perform type checking. Any errors you get will be in terms of the template instantiations, not your original code.

Unlike C++, Rust has a type system for the full language. In particular, traits are type checked once and you get errors in terms of the code you wrote yourself. There are other compilers that use complete monomorphisation, such as the MLton compiler for Standard ML, where you don't hear horror stories about terrifying error messages because your code was checked before being specialized.

I want to stress that this is a terrible design decision in terms of usability, because it is incredibly attractive from an implementation perspective. Parametric types are a delicate issue in an imperative language and always end up rejecting some perfectly fine programs. Monomorphisation both eliminates this issue and potentially leads to more efficient code, but your type errors will suffer.

I could say more about this - there are more trade-offs involved - but actually working with template heave C++ code is a better argument than anything I could say.

...That said, the examples in the blog post actually don't use any complicated machinery, and the quality of the compiler errors just comes down to very good engineering on the part of the Rust development team. :)

Re: The Rust borrow checker from a different perspective

#28
I just sent this around to my entire (mainly Java) team. I think it might be the best intro to describe the fundamental memory model that I’ve read.

The walk through this basic example, showing interface changes that enforce different aspects of the code, are wonderful. It took me a long time to grok some of these basic concepts, and this explains them so concisely.

I might use this as an intro to Rust for anyone who starting to learn the language. Kudos to the author for expressing these concepts in such elegance.

Re: The Rust borrow checker from a different perspective

#29
That's a nice way of introducing type state programming in an affine programming language!

In general though, type state programming would be even nicer in a linear language. For example, in the http server I could start writing a response without ever writing a body. Rust would happily accept this code, because Rust is always allowed to throw away resources.

An affine language controls the duplication of resources, while a linear language controls both duplication and destruction of resources. In a linear Rust, a data structure would have to implement Drop in order to be silently deleted and in the http server "HttpResponseWritingHeaders" should not implement Drop.

Re: The Rust borrow checker from a different perspective

#30
post #12
post #10

From that writeup, it seems like one is forced to program Rust in an object oriented manner. Is it possible to program it in a functional manner like Lisp, or a procedural manner like BASIC or assembler?

Just to be clear, rust is not object oriented. You do not use inheritance nor do you have objects. There was a recent post comparing rust to haskell https://www.fpcomplete.com/blog/2018/10/is-rust-functional . And tldr is yes it lends itself to a functional style...

  lorry.pickup(&rustfest_envelope);
 lorry.done();
looks like methods on an object ("lorry") to me. Is it something else?
Post reply on HN