Live data from Hacker News

My favorite Rust function

blog.jabid.in

21–30 of 197 posts

Re: My favorite Rust function

#21
Reminds me of Coq's definition of `False`:

`Inductive False := .`

i.e., `False` has no constructors and hence is an empty type.

Anyway, this means that for any type `A`, you can construct a function of type `False -> A` because you just do this:

`fun (x : False) => match x with end.`

Since `False` has no constructors, a match statement on a value of type `False` has no cases to match on, and you're done. (Coq's type system requires a case for each constructor of the type of the thing being matched on.) This is why, if you assume something that is false, you can prove anything. :)

Re: My favorite Rust function

#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.

Re: My favorite Rust function

#23
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.]

It depends: if add1 borrows the value then this code is fine. If add1 takes ownership of the parameter then no, this will not work.

What do you mean by "will not work"? Will it not compile? And how do we write that with add1 taking ownership vs. not?

Re: My favorite Rust function

#24
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.]

Normally yes, if using an object. In this case, the integer types implement the Copy trait, so instead of actually having your first call to add1 take ownership of x, it will just operate on a copy of the value, so your second call will work too.

Re: My favorite Rust function

#25
Do variables go out of scope after last use or when the function exits? I could see the former evolving into the language if it’s not already the default behavior.

In which case there’s only one situation where I could see this useful, and that’s when you are building a large object to replace an old one.

The semantics of

    foo = buildGiantBoject();
In most languages is that foo exists until reassigned. When the object represents a nontrivial amount of memory, and you don’t have fallback behavior that keeps the old data, then you might see something like

    drop(foo);
    foo = buildGiantBoject();
Most of the rest of the time it’s not worth the hassle.

Re: My favorite Rust function

#27
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.]

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 cause a compiler error) or a mutable reference (i.e. `&mut x`), which will allow you to access or mutate the data in `x` but not take ownership of it (unless it's replaced with something else of the same type).

However, a few types, including integers, but also things like booleans and chars, implement a trait (which for the purposes of this discussion is like an interface, if you're not familiar with traits) called Copy that means that they should be implicitly copied rather than moved. This means that in the specific example you gave above, there would not be any error, since `x` would be copied implicitly. You can also implement Copy on your own types, but this is generally only supposed to be done on things that are relatively small due to the performance overheard of large copies. Instead, for larger types, you can implement Clone, which gives a `.clone` method that lets you explicitly copy the type while still having moves rather than copies be the default. Notably, the Copy trait can only be implemented on types that already implement Clone, so anything that is implicitly copied be can also be explicitly copied as well

Re: My favorite Rust function

#28
post #23

Earlier quoted context omitted.

It depends: if add1 borrows the value then this code is fine. If add1 takes ownership of the parameter then no, this will not work.

What do you mean by "will not work"? Will it not compile? And how do we write that with add1 taking ownership vs. not?

It won't compile, because the compiler keeps track of where you moved the value and notices that x now has no value. If you want the function to borrow the argument instead, make it take a reference, &x.

Re: My favorite Rust function

#29
post #23

Earlier quoted context omitted.

It depends: if add1 borrows the value then this code is fine. If add1 takes ownership of the parameter then no, this will not work.

What do you mean by "will not work"? Will it not compile? And how do we write that with add1 taking ownership vs. not?

You'll get a compile-time error explaining the situation, yes, including documentation links and possible fixes. The differences between the versions are minimal.

    fn add1(x: Thing) -> Thing {
      x + 1
    }

    fn add2(x: &Thing) -> Thing {
      x + 2
    }
This works on the assumption that whatever "+ 1" really means doesn't itself need ownership of x, e.g. because it mutates it. If it does, then you'll get an error. Or you could do this:

    fn add3(x: &mut Thing) {
      x.add(3)
    }
Which, presumably, modifies x instead of returning a copy. It's your job to make sure it makes sense to do that, but Rust has another trick in its pockets:

    let y = add1(x);  // Works; takes ownership.
    let z = add2(&y);  // Works, and doesn't take ownership.
    let zz = add2(&y);  // So you can do it twice. (Y tho?)
    let h = add3(&y);  // Compile-time error!
    let hh = add3(&mut y);  // Works. You need to specify that you're fine with y being mutated.

Re: My favorite Rust function

#30
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 to complain about this, but it's completely valid C++ (because move leaves the object in an unspecified but valid state). In Rust, the argument will be automatically moved in the first call, and the second call will generate a compile-time error.
Post reply on HN