3 ms·
What is the forest here (asking genuinely)? Language usage ergonomics? I think it's fine like this, because most of the time the language makes it easy to wrap
by spoiler 5y ago
What is the forest here (asking genuinely)? Language usage ergonomics?
I think it's fine like this, because most of the time the language makes it easy to wrap/convert a writer into a buffered writer and use that instead.
Also, it preserves the principle of least surprise. If I call write on a Writer, I expect it to write immediately. When I want it to be buffered, I would either look for it in the stdlib (and in Rust's case expect to find it) or look for a lib (sans implementing it)
- PaulDavisThe1st 5y ago> If I call write on a Writer, I expect it to write immediately. But you can't know what "to write" actually means. When you call write(2) on a POSIX system, what is it that you think happens immediately? Patterns of NVRAM storage are changed? Fluctuating voltage on a cat-5 cable wobbles a bit in response? OK, maybe you can get away with saying "If I call write on a Writer I expect it to hand over the data to the kernel immediately", and that's somewhat reasonable. The problem is that such a design encourages beginner programmers to use unbuffered IO, which is generally the wrong thing to do.
- spoiler 5y agoThey're not encouraged to use unbuffered io. The documentation for FS and IO both point you in the direction of BufRead and explain why you might want it (in the intro section of each).
- BoorishBears 5y agoI mentioned what least surprise is to me: I'm used to a raw stream allowing a few specific operations, if it only allows: - read/write/peek one single byte - write a byte array, but you must explicitly specify length and offset I assume it's unbuffered. Usually those are the building blocks of a buffered stream API after all. It also matches write(2) closely (offset replaces pointer math) What surprises me is that it takes a full byte string and writes it with no hints about offset or length, usually that's only provided with buffered interfaces. - Again, there's no written rules about it, I'm just sharing my experience from writing code in a lot of different languages. Someone else might have an experience with a different set and have no such expectation, so I'm not saying Rust is wrong, I'm instead pointing out why this isn't a "well duh" moment
- spoiler 5y agoI 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.