My personal devlog.
You can subscribe to this page via RSS.
This page lists entries for the year 2021, for past entries consult the devlog archive.
TLDR; use path.Join when working with io/fs.FS instead of filepath.Join. Always read the docs before you start using some the codes - specially if you’re dealing with os specific resources.
Quote from doc:
Note that paths are slash-separated on all systems, even Windows. Paths containing other characters such as backslash and colon are accepted as valid, but those characters must never be interpreted by an FS implementation as path element separators. - https://golang.org/pkg/io/fs/#ValidPath
I have been recently working on https://git.io/node_manager this project mostly from Linux. Some of the code uses the newly introduced (1.16) io/fs package - a read-only abstraction of a filesystem. There’s around 30 unit/integration tests between the different parts of the module and all of them were passing.
However, as I pulled the code on my Windows system today and re-ran the whole test suite, I quickly discovered that some of the tests were failing - the ones that interacted with the io/fs.FS. Debugging through the code, I found that filepath.Join was joining the path segments with \, I suddenly remembered that io/fs works with only / (forward slashes, even on Windows). Had I not read the doc before, this may have cost me a few hours of useless fiddling around/debugging.
PS. I highly recommend trying out the fstest package as well if you’re testing io/fs stuffs. And do use the io/fs package if all you’re doing is reading from files/dirs. This is such a nice abstraction - the backend filesystem can live on a single file/local filesystem/database/ftp/cloud storages/s3/etc.
More reading:
path.Join - “The path package should only be used for paths separated by forward slashes, such as the paths in URLs. This package does not deal with Windows paths with drive letters or backslashes; to manipulate operating system paths, use the path/filepath package.” - https://golang.org/pkg/path/
filepath.Join - “The filepath package uses either forward slashes or backslashes, depending on the operating system. To process paths such as URLs that always use forward slashes regardless of the operating system, see the path package.” - https://golang.org/pkg/path/filepath/
Hello, 2021!
I’ve been experimenting with Rust for a while and I really like the language. I like how it forces me to check for all the possible values when I’m matching on something or checking a condition. Compile time checks, although quite annoying at times (because of my inexperience obviously) is so.. so much better than a dynamically typed language. I no longer mistakenly get variable undefined errors or a missing comma/semi-colon error at runtime only if the code gets inside a certain condition! There are so much things to like about the language honestly. Some of my favorites are:
structs and impls blocks. I like how the impl blocks are separate from the actual struct definition. This allows for a cleaner code and lets me see what the actual data is about without having to see the method definitions. You can also have multiple impl blocks for each category of features that you want to support. For example, one block for only initializing the struct (maybe two to three different types of initialization methods). Implementing traits also happen in different blocks. This keeps the code really clean and readable.
Everything is by default immutable. You cannot assign to any variable unless it is explicitly marked as mutable. The same goes for function calls and definitions. You must mark a parameter as mutable to mutate it inside a function. When you call that function, you are also required to use the &mut keyword to pass as a mutable reference. Immutable references are represented by &. This makes for a code that is very explicit in that, you don’t have to think whether the variable that you just passed will be mutated by the function, or just read value from it, or maybe own it completely.
Error messages! I think this is one of the most underrated and underappreciated features of Rust that many people just take for granted. This is by far the most friendly language I’ve seen in terms of error messages. Although there are cases where the error messages can go out of hand just like other languages, it generally highlights where you made a mistake, what the possible fix might be and sometimes an additional error code to let you know more about how it originates and the possible solutions.
Very explicit about type conversions. Unlike many other languages, Rust does not auto convert from one type to another unless you explicitly ask it to do so. You are then also forced to check if the conversion succeeded (unless you do away with if let or do unwrap()/expect(...)).
enums! They can hold data! You can easily match on a particular enum variant and extract data from it. The match keyword will match exhaustively, meaning, the compiler will not let you go away with just by checking for one variant and ignoring the other ones (although you can with if let or using _ => () in match blocks).
Cargo! Although not directly part of the language itself, Cargo is really friendly for a compiled language and the dependency management is nice (I’ve only tried “Rust only” simple projects, not the ones where you have to link to other languages/libs at compile/runtime). My experience with cargo is very limited, by so far I love it!
Docs and Tests! The whole rust community is obsessed with extensive documentation and tests. As a result, almost all the popular crates (what you call modules in python or external libraries in other languages) have really good documentation with example code. The standard library contains code blocks which let you run the code right in the browser. The documentation site for every rust crate is of the same style, so you don’t have to figure how docs are laid out in the different projects. They even have docs.rs which hosts the documentation for all the uploaded crates on crates.io.
High quality libraries. Rust libraries are some of the most highest quality libraries out there. Of course this is not true for every library but for the most popular ones, this is true. They’re usually battle tested and runs in production in many organisations. The popular library maintainers usually strive for correctness, safe and fast code.
There are obviously many more, but tbh I’m not really that much of an expert in Rust yet so I can’t say more. There are also challenges that I have not faced yet that many experienced and big projects face. I also haven’t really grasped the lifetimes concept to the core.
With this limited knowledge, I set out to build a Websocket server that lets us display logs in real time. The client software that runs on one machine must be able to send logs to a browser that is possibly running on another machine. The client machine cannot host a server directly because of security reasons. So, the flow of messages will be: Client -> Server -> Browser. I was able to successfully build this system, however its yet to be put into production and test. Maybe I’ll write about this more about this in another post?