News for hackers. Stuff that matters.
Re:Fast Rust Builds by scottlamb on Sunday September 05, 2021 @02:01PM (#28426277)
Partly personal preference in both what is readable and in how much you optimize caller convenience vs simple callee code. I think it's fine to just expect the caller to do the conversion. In fact, it's often automatic. (Eg if you have a PathBuf, then just sticking a & in front of it is sufficient to supply &Path via Deref<Target = Path>.) So I'd write this function as follows:

    fn read(path: &Path) -> io::Result<Vec<u8>> {
        let mut file = File::open(path)?;
        let mut bytes = Vec::new();
        file.read_to_end(&mut bytes)?;
        Ok(bytes)
    }
which I find readable. Comparing to another language...I haven't written Go in a while, but I think the rough equivalent would be:

    func Read(path *Path) ([]byte, error) {
        file, err := os.Open(path)
        if err != nil {
            return nil, err
        }
        slice, err := io.ReadAll(file)
        if err != nil {
            return nil, err
        }
        return slice, nil
    }
(Maybe in this case you'd just return the io.ReadAll rather than doing an if, but I'm trying to use similar constructs. The ? is something you can use all throughout the function, not just at the end, so I tried to match that pattern.)

[edited: I said there wasn't a io.ReadToEnd extracted from io.ReadFile in Go, but jatone pointed out it's called io.ReadAll.]

Re:Fast Rust Builds by lenkite on Monday September 06, 2021 @04:29AM (#28431634)
I never understood why Rust requires '&mut' while passing parameters.

        file.read_to_end(&mut bytes)?;
`bytes` is already mutable. `read_to_end` is already declared as taking a `&mut Vec<u8>`. So why force every caller to do a `&mut` un-necessarily ?
Re:Fast Rust Builds by MathiasPius on Monday September 06, 2021 @04:51AM (#28431725)
It's so that a reader of the calling code can immediately reason about which calls might be mutating state. If you have 10 calls, all taking &bytes, any of which may or may not be mutating the data while others are merely reading it, it becomes very difficult to reason about.

By being explicit about this, Rust becomes easier to read, but (slightly) harder to write.

Re:Fast Rust Builds by zozbot234 on Monday September 06, 2021 @05:32AM (#28431950)
Shared borrows and unique borrows also behave very differently in Rust. '&mut' for unique borrows is a bit of a misnomer, because passing some objects via a shared '&' reference might also mutate state 'indirectly' via internal mutability.
Re:Fast Rust Builds by masklinn on Monday September 06, 2021 @11:15AM (#28434495)
> So why force every caller to do a `&mut` un-necessarily ?

Because it's not unnecessary? `bytes` is a `Vec`, not a `&mut Vec`, those are different things, and Rust generally avoids magically doing things under the covers. So it doesn't auto-ref (let alone auto-mut-ref) parameters, only subjects.

Re:Fast Rust Builds by oxnrtr on Sunday September 05, 2021 @05:15PM (#28427876)

    fun read(path: &Path): io.Result[Vec[UInt8] {
      var file = File.open(path).orReturnErr()
      var bytes = Vec.new()
      file.readToEnd(&mut bytes).orReturnErr()
      Ok(bytes)
    }
would be good enough for me, tbh.
Re:Fast Rust Builds by yakubin on Sunday September 05, 2021 @06:18PM (#28428329)
> File.open(path).orReturnErr()

Method syntax is reserved for functions. There are no method syntax macros. Since a return in a function terminates execution of this function, and not its caller, your example would not work.

Re:Fast Rust Builds by zozbot234 on Sunday September 05, 2021 @07:21PM (#28428714)
Right, so this should really be .orReturnErr , by analogy with .await . We just overload the dot symbol to mean both "method call" and "weird monadic stuff is happening here".
Re:Fast Rust Builds by simon_o on Monday September 06, 2021 @06:48AM (#28432409)
Exactly. ! for macros was a mistake anyway, I guess that's why they stopped using it with await.
Re:Fast Rust Builds by yakubin on Monday September 06, 2021 @07:28AM (#28432656)
It wasn't. Without "!" it isn't obvious that you cannot take a:

  let a: Result<u8, u8> = /* whatever *;
  let b = match a {
      Ok(v) => Ok(foo(v)),
      e => e,
  };
and transform it into:

  let a: Result<u8, u8> = /* whatever *;
  let b = a.map(foo);
if "foo" is a macro. When you substitute "foo!" for all the "foo" above, it is obvious.
Re:Fast Rust Builds by simon_o on Monday September 06, 2021 @07:32AM (#28432670)
Perhaps macros shouldn't do things that aren't "obvious" then?
Re:Fast Rust Builds by infogulch on Monday September 06, 2021 @09:11AM (#28433316)
As a rule, rust rejects the "simpler" choice that instead lay the burden of correctness at the feet of the user. I'm glad they keep a hard line here.
Re:Fast Rust Builds by steveklabnik on Monday September 06, 2021 @11:43AM (#28434805)
> I guess that's why they stopped using it with await

await cannot be a macro, it has to be built into the language, and so it doesn't have the ! because it's not one.

Re:Fast Rust Builds by simon_o on Monday September 06, 2021 @05:50AM (#28432056)
You realize the syntax has changed more than that?
Re:Fast Rust Builds by yakubin on Monday September 06, 2021 @07:21AM (#28432604)
Yes, but "->" vs ":", or kinds of brackets are superficial, while method syntax has a specific meaning.
Re:Fast Rust Builds by scottlamb on Sunday September 05, 2021 @07:15PM (#28428668)
These differences are mostly superficial. We all need to decide if we're going to avoid a language because its punctuation choices aren't "good enough" for us or if we're going to choose the best tools for building useful systems.

"orReturnErr()" seems quite against the Rust philosophy though — refusing the method call syntax for early return means folks can easily miss something important. Rust uses ? for error handling, ! for macros, and keywords (with no parenthesis) for other control flow. I prefer it that way.

Re:Fast Rust Builds by capableweb on Sunday September 05, 2021 @02:07PM (#28426344)
I don't know Rust but converting that code snippet of Go code you wrote to (non-idiomatic [wouldn't use upper-case characters, namespaces used doesn't actually exists]) Clojure:

    (defn Read [path]
      (io/ReadToEnd (os/Open path)))
Now that's what I call readable!
Re:Fast Rust Builds by scottlamb on Sunday September 05, 2021 @04:14PM (#28427376)
You can chain calls in Rust. If read_to_end were written to allocate and return a fresh Vec, you could do this:

    File::open(path)?.read_to_end()
but instead read_to_end is written to take a presupplied Vec so the caller has the option to reduce allocations.
Re:Fast Rust Builds by masklinn on Monday September 06, 2021 @11:18AM (#28434531)
> Now that's what I call readable!

Unsurprisingly APIs can be more readable when you don't ponder why they're designed the way they are and completely ignore their purpose.

The `read_to_end` API[0] was designed such that the buffer could be reused in order to avoid unnecessary allocations.

There are helpers for common tasks, though people may not know or notice them. In the case of `Read::read_to_end`, it's called std::fs::read[1], which actually preallocates the vector based on file size internally so might be faster than hand-rolling `read_to_end`.

So here's your code in Rust, for the last 3 years or so (since 1.26 was released):

    fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
        std::fs::read(path)
    }
[0] and all of the Read trait in general

[1] https://doc.rust-lang.org/std/fs/fn.read.html

Re:Fast Rust Builds by brundolf on Sunday September 05, 2021 @02:52PM (#28426715)
Clojure is also a much higher-level language than Rust (and arguably Go too)
Re:Fast Rust Builds by jatone on Sunday September 05, 2021 @03:36PM (#28427101)
bytes, err := ioutil.ReadAll("path.txt")

is the golang equivalent.

Re:Fast Rust Builds by steveklabnik on Sunday September 05, 2021 @03:52PM (#28427212)
To be clear you can write

    let bytes = std::fs::read(“path.txt”)?;
If you’re trying to actually do this in real code. The example is how you’d write this function yourself, but if you’re just trying to Get It Done, it’s significantly easier than the example.