Somehow, in my Rust career, I've mostly managed to avoid APIs like this. I'm writing some functions that operate on files. Some of them take files as input, purely for reading; some take them as output, in which case they're expected (but not required) to be empty, and will be written to. In the first case, the file cursor may be left in an undefined position by any of these functions. In the second case, the files cursor and contents are undefined (though the cursor will likely be at the end). In each case, should I be taking File
, &File
, or &mut File
?
If possible, you should make your functions generic instead of accepting any form of File
specifically.
fn do_thing_to_file<T: io::Read + io::Write + io::Seek>(file: T) {
...
This allows callers more flexibility, e.g. to use io::Cursor
for in-memory storage, or some kind of virtual or network filesystem. Then, your question is moot because they can pass File
, &mut File
, or &File
as they see fit. (If you wanted to particularly encourage not consuming, you could require &mut T
.)
Then, whether or not you can actually make the function generic, you should document the state the file is left in, or lack of specified state. (It doesn’t entirely make sense for consuming File
to convey that the cursor is not in a usable state, because multiple File
s can share a cursor.)
True, that works for almost all my use-cases. There is one situation in which case I mmap a file[1], and want to keep that burden off my users... but I suppose I could afford to make that just a byte slice. Thanks, I use Seek
so rarely that I forgot that I could bound on it like that.
I'm using
bsdiff
, which takes slices ↩︎