So, my favorite thought process from the Python 2 to 3 transition comes from staring deeply into the hubris of the Python 3000 effort itself as wanting to insist "everything is Unicode"; on the one hand they were trying to shame developers using Python 2 for using it incorrectly (even though it was totally possible to handle Unicode correctly with that stack), yet on the other they were pumping out ridiculous unavoidable security SNAFUs in their API surface by assuming that filenames on disk are UTF-8 :/.

To tell another story of the importance of reading the--in this case, not even long!--specification: on Windows, filenames are stored as UCS-2 (as it all predates UTF-16), and is most likely most correctly modeled as an arbitrary sequence of non-0 16-bit values; meanwhile, on Unix systems, a filename is pretty much just an arbitrary sequence of non-0 8-bit values (with some weirdness around the slash character... the API surface doesn't support it really but sometimes the filesystem does). The premise that filenames have a display encoding mostly comes from the software rendering the strings and is then defined in terms of a bunch of hacks involving "locales", and isn't a property of the underlying API or storage semantics.

You can't assume in either case that they are canonical Unicode representations, and so storing them as "strings" in your program is super wrong and can lead to anything from data storage mistakes to security vulnerabilities. The Python 3 API filesystem API was thereby so bad that it would do things like just skip over files during directory traversal that didn't map to its attempt to decode stuff as Unicode. Like: if you wanted to hide a file from a Python 3 program, you could just make it invalid Unicode :/.

(I realized people reading this comment might find a reference on this fun, and so found this seemingly-comprehensive postmortem. I haven't read it, but it at least looks really good ;P.)

https://vstinner.github.io/python30-listdir-undecodable-file...

I thereby find the whole Python 3 debacle extra demoralizing as it felt like a bunch of people who didn't truly understand how hard the problem they were dealing with was--and how much of the world actually is binary, and how often (when dealing with things like filesystems and network protocols) parsing binary sequences as pseudo-strings is "correct"--both sometimes-correctly insisting everyone was wrong while themselves failing to build something even minimally useful until like, Python 3.4 or so; and, in the end, I feel like they just ended up creating a "new mess" by having filenames now encoded as "Unicode strings"... but with weird exceptions for allowing round-trip behaviors of strings that couldn't be decoded :(.

The reality is that there are a number of things that developers work with which are pseudo-strings, and languages should provide good tooling for working with them. We see the same issue come up with HTTP headers and filenames: things that are actually binary but have string-like structure. There is a reason why both C++ and Python 2 are often looked at fondly by low-level developers for providing low-level string representations, even while they end up encouraging broken behavior higher up the stack. I haven't looked enough at Rust's standard library to see if they truly nailed this yet, but maybe one day we will have a language that encourages the right behavior for these mechanisms that are neither strictly string not strictly binary.