4 ms·
I think it doesn't ask for length because it would increase the chance of mistakes happening. You don't really need to pass the length "manually" because its en
by spoiler 5y ago
I think it doesn't ask for length because it would increase the chance of mistakes happening. You don't really need to pass the length "manually" because its encoded in the value already. Not sure what you mean by offset, but you could just index with a range probably (ie `bytes_buff[n..]`), but that won't work for Strings (because they're internally encoded as UTF-8, but you could use `.chars()` and then the iterator methods on it to select which part of the string you want to write).
- BoorishBears 5y agoThis comment is kind of making my point. Unbuffered APIs are meant to be tricky: They ask for length (even in languages where arrays have a fixed length like Java) because the idea is: - you're either building out a buffered wrapper and can't rely on the encoded length value (because your array length will be the entire buffer while you might only want to write part of it) - or you want very precise control over the writes and are willing to risk those mistakes. So they need to open up that risk of a mistake. It's an API that mirrors to the user how the underlying syscall operates, so the underlying complexity is being very plainly exposed. - Usually it's the buffered wrapper that will have a convenience method that takes a String or a full array without additional information, or there'll be an explicit call to force a buffered wrapper to use a 0-sized buffer. C++: Without explicit call to setbuf, system defined default buffer used (matches POSIX behavior) Java: FileWriter vs FileOutputStream, FileOutputStream is unbuffered will not take a string without manually encoding it to bytes first, FileWriter will take a string and is buffered Python2: need explicit 0 sized buffer Python3: need to flag as unbuffered and automatically converts file to binary mode, so calling write("SomeString") will not be allowed with unbuffered output. C#: FileStream buffered by default, must explicitly ask for 0-size buffer Obviously this won't hold across every language. But you can see that languages consistently make it easier to do the "right" thing in this case, while Rust isn't. I'm not necessarily holding it against Rust either to be clear, but I'm holding it against the droves of comments here that are acting like this is such natural behavior you'd be a rookie to not catch it... it's been a loooong time since most people have used a language where that was "easy" behavior to uncover accidentally. Like I said even C++ went out of it's way to improve things in this regard.
- spoiler 5y agoRight, I see. I guess I understand where you're coming from (basically your expectations are shaped by other languages). That's fair enough, and I've had some of my expectations broken fore the same reasons in Rust, and it was around ergonomics too. Rust's solution to the problem, even if a departure from traditional solutions, is more ergonomic, and I like that. There's no reason buffered or buffered IO needs to be trickier than the other! Why sacrifice one? Sure you could have Write buffered by default and have UnbufferedWrite instead, but I personally find this "reversed API" idea really weird. It seems like a subtractive API rather than an additive one, and it also doesn't compose as nicely. Your argument about size proves my point too, so we're going in circles I guess. I'll summarise why I prefer Rust's approach (sans ergonomics): it's harder to introduce logic errors if all the information can be inferred from the value itself. If I want to send "hello", and somewhere I fucked up, so I'm thinking 4 bytes instead of 5 and now I sent "hell", and threw 'o' to the RAMs. Why? I can just tell it I wanna send "hello", and it does it. It's more robust. If I wanted to send just "hell" (assuming it's bytes) in I can just create a slice to the necessary data and pass that. The slice explicitly itself signals to other devs I wanna send a subset of the buffer, too. Doing it this way, buffered or unbuffered doesn't really matter: The API stays the same. Adding buffering is usually a one line change of converting your writer to a buffered writer. Someone called Rust's approach an "arcane incantation", if anything the only magical things here is the special/magic sized buffers or numbers in other languages you highlighted. Furthermore, I think half of the existing implementations probably didn't even think about this design decision at all, they just inherited it. At the end of the day, this discussion about buffering isn't super important in the grand scheme of things. The stdlib gives you just building blocks (Rust has great ones, IMO) to build a networking library you want, so your (un)buffering is going to be tucked away in a couples of places and you'll be using your own `report` or `send` or `set` methods instead of a sprinklings low-level write/read syscals everywhere anyway.
- Measter 5y agoThe `write` function here comes from the `Write` trait[0]. This isn't the unbuffered write API, it's just the write API. What it's writing to could be a `File`, or it could be `StdOut` or a `Vec<u8>`. It also doesn't take a string; it takes a slice of bytes. In Rust, prefixing a string literal makes it a `&[u8]` instead of a `&str`, which is why you can just pass it straight into `write`. Adding a length field would only make it more error-prone to use without any added benefit. Slices already know their length, and subslicing is trivial. If you have a larger slice, and you only want to write a subsection of it, you just index it. Forcing the user to pass in two lengths is just silly, and all the API implementations would do is subslice the input to that second length anyway. You could argue that `File::open` and `File::create` should return a `BufReader<File>` and `BufWriter<File>` respectively, and I would agree that this would be reasonable as the most common use-case probably benefits from buffered IO. However, that ship's already sailed, and those functions can't be changed. The description for the `File` type should also more explicitly state that IO is unbuffered. I'm surprised it doesn't, the docs are typically better at that sort of thing. [0] https://doc.rust-lang.org/std/io/trait.Write.html https://doc.rust-lang.org/std/io/trait.Write.html