Live data from Hacker News

My favorite Rust function

blog.jabid.in

171–180 of 197 posts

Re: My favorite Rust function

#171

Earlier quoted context omitted.

> Enumerations type myEnum int const ( someValue myEnum = iota anotherValue moreValues ) > Sum types Consider thinking about the types in your sum type and what they have in common, and define an interface that they share. > Recursive types Not sure what you mean. This is valid Go: type tree struct { value int left *tree right *tree } > Parametric polymorphism Yes Go sucks in this area. `sort.Interface` is a good exa…

> Enumerations - those are integers not enumerations. In Golang they have a type Bool which is either true or false. Can I define my own type using the same convention in GoLang? No. I can't. Ideally I want the ability to do this: type Bool = Enum { True False } or type Tri = Enum { True False QuantumTrueFalseDuality } But in Golang I'm locked in with what the creators provided as primitive types. I can't go deeper.…

Those above are not integers but type that you have to explicitly cast integers to if you really want. Please don't make uninformed comments.

Re: My favorite Rust function

#172
post #146
post #42

Earlier quoted context omitted.

The compiler prevents you from implementing both Copy and Drop (i.e. a destructor). So dropping any copy type is a no-op.

Lesson learned from c++ "rule of five"? Where if you implement a destructor you must also carefully implement a copy constructor so that the two copies of the object don't accidentally refer to each others members in any way. Something that is much harder than it sounds like, leading to the now more recommended "rule of zero" saying just don't.

Rust doesn't have copy constructors. By hard rule, any normal (non-Pin) rust struct can be safely moved by doing a memcopy of it's storage to a different, correctly aligned memory location. Structs that are Pin just can't be moved at all. Implementing Copy is just a marker that means that after doing the memcopy, the original can still be safely used.

This rule really helps both the compiler and the programmer to make moving and copying things effortless. It does prevent things like a struct holding internal pointers to it's own state. This is not bad on x86, because you can replace internal pointers with internal offsets, and the instruction set contains a fast reg+reg addressing mode, but can cost an extra instruction on many other cpu architectures. IMHO the rule is well worth it, although it is an example of a situation where Rust chooses to give away a bit of performance for sanity.

Re: My favorite Rust function

#173
post #16

I haven't used rust, so can you explain this to me: If I do the rust equivalent of: def add1(x): return x + 1 x = 1 y = add1(x) z = add1(x) then will x have been deallocated by the first call to add1 and will the second call to add1 fail? [You can ignore the fact that I'm using numbers and substitute an object if that makes more sense in the context of allocating / deallocating memory in rust.]

If add1 takes ownership of the argument, yes (and x is not implicitly copyable). Compare with C++, in particular types with deleted copy operators (e.g. unique_ptr ). In order to call a function that takes an unique_ptr by value as argument, you must explicitly move the object into the function: void foo(unique_ptr x) { ... } unique_ptr x = ...; foo(move(x)); foo(move(x)); Linters (i.e. clang-tidy) can be configured…

> because move leaves the object in an unspecified but valid state

Also and importantly because move() only indicates that the value may be moved from (it's a fancy cast), that doesn't mean it will be moved from. The called function needs to take an rvalue reference (T&&) for a move to possibly happen. That is if you had a

    void foo(unique_ptr&& x)
it may or may not move. Likewise you can call move() on things which are not move constructible (or passing the result to functions which don't care) with no ill effects (save to the reader).

IIRC what happens here is that since foo() takes a unique_ptr by value the compiler will insert the construction of a temporary unique_ptr and overload resolution will select the move constructor (since we've provided an rvalue reference, and assuming the wrapped type is move-constructible).

> move leaves the object in an unspecified but valid state

Also notes that this is the general semantics, but specific types can define their exact behaviour here. In particular, unique_ptr specifies that a moved-from unique_ptr is "nulled" (equal to nullptr).

Re: My favorite Rust function

#174
post #104

Earlier quoted context omitted.

> Maybe you're referring to --release compilation? Indeed.

Ok, well, "cargo build --release" took 3 minutes and 55 seconds, which I would consider reasonably fast for compiling and optimizing 300+ libraries. I guess your laptop has only 2 cores in an older processor, which would explain the difference. Kudos to you for keeping good hardware alive.

sometimes it takes 100x more without any apparent reason - it's probably because of memory or something, what takes minute to compile on my MBA, sometimes never finishes on raspi3 (swap is off)

I currently compile on raspi4 and scp the results there to check.

Re: My favorite Rust function

#175
post #45

For me, rust is still love & hate, even after 1 year of half-time (most of the free time I have) hacking. It's a wonderful language but there are still some PITAs. For example you can't initialize some const x: SomeStruct with a function call. Also, zero-cost abstraction is likely the biggest bullshit I've ever heard, there is a lot of cost and there's also a lot of waiting for compiler if you're using cargo packages…

> Also, zero-cost abstraction is likely the biggest bullshit I've ever heard, there is a lot of cost and there's also a lot of waiting for compiler if you're using cargo packages. Zero cost refers to runtime cost, not compilation cost. Zero cost abstraction is not bullshit.

Yes, I know, and all those crates trying to wrap unsafe C libs are really not free - I've tried.

Maybe just dig into some sources and see what macros are expanded to to see the overhead.

Re: My favorite Rust function

#176
post #27

Earlier quoted context omitted.

Interestingly, the type of `x` actually does matter here in Rust! For most types, yes, passing something by value into a function will cause the memory to be "moved", which means that reusing `x` will be a compiler error. That being said, you can also either pass a shared reference (i.e. `&x`), which will allow you to access the data in Rust (provided you don't move anything out from it or mutate it, which would caus…

Is there any kind of compile-time check available for this (e.g. BIG COMPILER WARNING when you pass-by-value something that lacks Copy)? Seems like a lot of unsettlingly Python-esque freedom ("read the docs and don't screw up") for a language like Rust.

Rust really doesn't do ("read the docs and don't screw up").

It's a very much a "bondage and discipline"-style language in the sense that unless you explicitly use "unsafe", you have to prove to the compiler that everything you do is safe. Moving large things is not unsafe because after you move something to a different scope, the original doesn't exist anymore and can't be used, so if you were to accidentally move something and tried to use it again, the compiler would helpfully tell you that the thing you're trying to refer to isn't there anymore.

There is no warning on every time you pass something without copy because passing things to different scopes is extremely common, normal and desired.

Re: My favorite Rust function

#177
post #22

> or making the language unacceptably crippled like Go Gotta say, I lost a lot of respect for the author at this point. It’s not like I don’t love Rust - quite the contrary - but if the only takeaway from Go for you is that it is “unacceptably crippled” then I feel you have missed a lot of insight. Go has been one of my languages of choice for over half a decade now, and for good reason.

Regardless of the merit of his criticism, it clearly derails the whole discussion about this blog post, which was just about a cool feature of Rust.

Re: My favorite Rust function

#178

Earlier quoted context omitted.

Is there any kind of compile-time check available for this (e.g. BIG COMPILER WARNING when you pass-by-value something that lacks Copy)? Seems like a lot of unsettlingly Python-esque freedom ("read the docs and don't screw up") for a language like Rust.

It is perfect fine to take by value something that is not Copy to transfer its ownership. It is a compiler error to use the value after it was moved. And the compiler error is quite explicit about how to solve it For example you can explicitly clone let y = add(x.clone()); let z = add(x);

What would be cases where the add() function need ownership of the x?

What the chance in real life that a function like that cannot simply use a reference?

In my learning Rust, my life became significantly better and easier when I started to references to borrow as much as possible.

Re: My favorite Rust function

#179
post #22

> or making the language unacceptably crippled like Go Gotta say, I lost a lot of respect for the author at this point. It’s not like I don’t love Rust - quite the contrary - but if the only takeaway from Go for you is that it is “unacceptably crippled” then I feel you have missed a lot of insight. Go has been one of my languages of choice for over half a decade now, and for good reason.

> > or making the language unacceptably crippled like Go > ... if the only takeaway from Go for you is that it is “unacceptably crippled” then I feel you have missed a lot of insight. Perhaps the author used a poor choice of words and instead could have phrased their intent along the lines of: Go lacks the semantic density needed to express solutions in both a concise and consistent manner. Were this the case, it wou…

> Go lacks the semantic density needed to express solutions in both a concise and consistent manner.

That is a difficult statement for a Go user to parse.

It is referring to a lack of language features that result in verbosity or boilerplate?

Re: My favorite Rust function

#180

Earlier quoted context omitted.

Hm. Ur right bad examples. The only advantage rust has then is more elegant syntax and safety for JSON. Ur also right about python and ruby. It is effectively the same. Overall though the need for assert is a sign of a crippled type system.

> Overall though the need for assert is a sign of a crippled type system. Given that most (if not all) new applications have to ingest data from third party systems and export outgoing data to other third party systems, the need for assertions and data validation is not going away, even in strongly typed systems. JSON is becoming the de-facto data transfer format, and thus JSON input validation is necessary in both w…

Uh no. Type assertion is not always required. This is false. Also type assertion is not a good thing in a strongly typed language. This happens in Golang exactly because the go type system is flawed. It doesn’t need to happen.

There is a huge difference in guessing a type using assertions or writing handlers within the boundaries of a domain. Alex’s point that writing such code is effectively the same as rust because the amount of code is the same. However safety is lost. There is literally no point of having a type system in place when unmarshalling Json in go because you have no type safety. In go if you assert the wrong type or if your missing a required assert the error may be hidden and if it’s not hidden you can only see it during runtime. In rust there is no type assertion, only type checking, so such a program won’t even compile.

Post reply on HN