As the sibling comment said it's not a C thing. It is done that way in C because you have to babysit allocations. Other languages aimed at ease of use will just allocate for you and give you the fully baked contents in a single fn call, but when you're writing code that 1) has to be called often enough where allocations can show up in your flame graphs or 2) the input is big enough that you can't realistically keep the entire thing in memory at once, you suddenly have to care about what the fn is doing internally. Making the default API for file interaction require you pass in your own buffer makes this explicit at the cost of some ease of use. Seeing this will also serve as a teachable moment, when the dev looks at the docs trying to understand why it has such an obtuse API, hopefully coming away with this comment's explanation. This might seem to be a pedantic distinction but I've seen Java codebases where buffer reuse was mandatory in the hot path and Rust is all about removing foot guns.