Is consuming `File` in an API unidiomatic?

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 Files can share a cursor.)

2 Likes

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. :sweat_smile:


  1. I'm using bsdiff, which takes slices ↩︎