<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Stdlib on</title><link>/tags/stdlib/</link><description>Recent content in Stdlib on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sat, 05 Jul 2025 00:00:00 +0000</lastBuildDate><atom:link href="/tags/stdlib/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 8: strings and bytes Builders — Stop concatenating in loops</title><link>/post/go/go-stdlib-strings-bytes/</link><pubDate>Sat, 05 Jul 2025 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-strings-bytes/</guid><description>&lt;p&gt;String concatenation with &lt;code&gt;+&lt;/code&gt; is one of the most common performance bugs I see in Go code reviews. Not because developers are careless, but because the bug is invisible — the code looks clean and correct. &lt;code&gt;result += piece&lt;/code&gt; is obvious. The quadratic memory behavior that follows is not.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;strings&lt;/code&gt; and &lt;code&gt;bytes&lt;/code&gt; packages are where Go provides the right tools for this job. &lt;code&gt;strings.Builder&lt;/code&gt; and &lt;code&gt;bytes.Buffer&lt;/code&gt; are both efficient for incremental string construction, but they&amp;rsquo;re not the same type and the choice between them matters in specific cases. Beyond building strings, these packages contain functions that have non-obvious but significant performance implications: &lt;code&gt;strings.Contains&lt;/code&gt; vs &lt;code&gt;strings.Index&lt;/code&gt;, &lt;code&gt;strings.Split&lt;/code&gt; vs &lt;code&gt;strings.SplitN&lt;/code&gt;, and the ones people reach for that they shouldn&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>Lesson 7: context Internals — How cancellation propagates under the hood</title><link>/post/go/go-stdlib-context/</link><pubDate>Thu, 22 May 2025 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-context/</guid><description>&lt;p&gt;&lt;code&gt;context.Context&lt;/code&gt; is the most important type in Go&amp;rsquo;s standard library that most people use by convention without fully understanding. You pass it as the first argument to functions. You check &lt;code&gt;ctx.Done()&lt;/code&gt; in goroutines. You create derived contexts with &lt;code&gt;context.WithTimeout&lt;/code&gt;. It works — until it doesn&amp;rsquo;t, and you have a goroutine that doesn&amp;rsquo;t cancel when the parent context does, or a context that leaks forever because you forgot to call the cancel function.&lt;/p&gt;</description></item><item><title>Lesson 6: sync Package Complete Guide — Mutex, Once, Pool, Map — when to use each</title><link>/post/go/go-stdlib-sync/</link><pubDate>Sat, 22 Mar 2025 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-sync/</guid><description>&lt;p&gt;The &lt;code&gt;sync&lt;/code&gt; package is where Go&amp;rsquo;s concurrency tools live when channels aren&amp;rsquo;t the right answer. &lt;code&gt;sync.Mutex&lt;/code&gt;, &lt;code&gt;sync.RWMutex&lt;/code&gt;, &lt;code&gt;sync.WaitGroup&lt;/code&gt;, &lt;code&gt;sync.Once&lt;/code&gt;, &lt;code&gt;sync.Pool&lt;/code&gt;, &lt;code&gt;sync.Map&lt;/code&gt; — each one solves a specific problem, and using the wrong one is a reliable way to introduce bugs or performance regressions. I&amp;rsquo;ve misused all of them at various points.&lt;/p&gt;
&lt;p&gt;The general guidance is channels for communication, mutexes for protecting shared state. But within the mutex family, the choice between Mutex, RWMutex, and sync.Map has performance implications that matter in hot paths. And sync.Pool is frequently misunderstood — it&amp;rsquo;s not a general-purpose object pool; it&amp;rsquo;s a GC-aware buffer recycler.&lt;/p&gt;</description></item><item><title>Lesson 5: os and filepath — Cross-platform file operations that actually work</title><link>/post/go/go-stdlib-os-filepath/</link><pubDate>Wed, 22 Jan 2025 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-os-filepath/</guid><description>&lt;p&gt;Go&amp;rsquo;s &lt;code&gt;os&lt;/code&gt; and &lt;code&gt;path/filepath&lt;/code&gt; packages are among the most underappreciated in the standard library. Most developers know &lt;code&gt;os.Open&lt;/code&gt;, &lt;code&gt;os.Create&lt;/code&gt;, and &lt;code&gt;os.ReadFile&lt;/code&gt;. Far fewer know &lt;code&gt;os.MkdirAll&lt;/code&gt;, &lt;code&gt;os.CreateTemp&lt;/code&gt;, &lt;code&gt;filepath.WalkDir&lt;/code&gt;, or the difference between &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;path/filepath&lt;/code&gt; — which is the difference between code that works everywhere and code that silently breaks on Windows.&lt;/p&gt;
&lt;p&gt;I maintain a CLI tool that runs on macOS, Linux, and Windows. The file operation bugs I&amp;rsquo;ve shipped have taught me exactly which parts of these packages require care.&lt;/p&gt;</description></item><item><title>Lesson 4: time Package Gotchas — Timezones, monotonic clocks, and the bug in your cron</title><link>/post/go/go-stdlib-time/</link><pubDate>Mon, 18 Nov 2024 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-time/</guid><description>&lt;p&gt;The &lt;code&gt;time&lt;/code&gt; package looks straightforward right up until the moment a bug report arrives saying &amp;ldquo;the nightly job didn&amp;rsquo;t run last Sunday.&amp;rdquo; It never runs on the Sunday when daylight saving time ends. Because that Sunday has 25 hours, and the cron expression fires twice at the ambiguous time, and the second firing is skipped because the code thinks it already ran. I&amp;rsquo;ve debugged this exact bug, and the root cause is always the same: someone — usually me — assumed time is simpler than it is.&lt;/p&gt;</description></item><item><title>Lesson 10: Conversion Traits — From, Into, TryFrom, AsRef</title><link>/post/rust/rust-stdlib-convert/</link><pubDate>Tue, 08 Oct 2024 15:30:00 +0000</pubDate><guid>/post/rust/rust-stdlib-convert/</guid><description>&lt;p&gt;I used to litter my Rust code with &lt;code&gt;.to_string()&lt;/code&gt;, &lt;code&gt;as&lt;/code&gt;, and manual conversion functions everywhere. Then I learned the conversion traits properly and my APIs went from clunky to clean. These traits are the glue that makes Rust code feel ergonomic — and understanding when to implement each one is the difference between a library that&amp;rsquo;s pleasant to use and one that makes people curse your name.&lt;/p&gt;
&lt;h2 id="from-and-into--infallible-conversion"&gt;From and Into — Infallible Conversion&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;From&amp;lt;T&amp;gt;&lt;/code&gt; defines how to create a type from another type. It can&amp;rsquo;t fail. If there&amp;rsquo;s any possibility of failure, you want &lt;code&gt;TryFrom&lt;/code&gt; instead.&lt;/p&gt;</description></item><item><title>Lesson 9: std::sync — Mutex, RwLock, Once, Barrier</title><link>/post/rust/rust-stdlib-sync/</link><pubDate>Fri, 04 Oct 2024 13:25:00 +0000</pubDate><guid>/post/rust/rust-stdlib-sync/</guid><description>&lt;p&gt;I shipped a data race to production exactly once. Go program, shared map, no lock. Took three weeks to reproduce — only happened under heavy load when two goroutines hit the same key simultaneously. The crash dump was useless. In Rust, that code wouldn&amp;rsquo;t have compiled. That&amp;rsquo;s not marketing — it&amp;rsquo;s literally how the type system works.&lt;/p&gt;
&lt;h2 id="arc--shared-ownership-across-threads"&gt;Arc — Shared Ownership Across Threads&lt;/h2&gt;
&lt;p&gt;Before we talk about locks, we need &lt;code&gt;Arc&lt;/code&gt;. You can&amp;rsquo;t share data between threads with just &lt;code&gt;Rc&lt;/code&gt; — it&amp;rsquo;s not thread-safe. &lt;code&gt;Arc&lt;/code&gt; (Atomic Reference Counted) is the thread-safe version.&lt;/p&gt;</description></item><item><title>Lesson 8: std::time — Duration, Instant, SystemTime</title><link>/post/rust/rust-stdlib-time/</link><pubDate>Tue, 01 Oct 2024 07:50:00 +0000</pubDate><guid>/post/rust/rust-stdlib-time/</guid><description>&lt;p&gt;A few months ago I tracked down a bug where a cache TTL check was wrong because someone compared &lt;code&gt;SystemTime&lt;/code&gt; values that had been serialized and deserialized across a system clock adjustment. The cached timestamps jumped backward, and suddenly entries that should&amp;rsquo;ve expired were &amp;ldquo;fresh&amp;rdquo; again. That&amp;rsquo;s the kind of lesson that teaches you the difference between monotonic clocks and wall clocks.&lt;/p&gt;
&lt;h2 id="two-clocks-two-types"&gt;Two Clocks, Two Types&lt;/h2&gt;
&lt;p&gt;Rust gives you two time types because computers have two fundamentally different clocks:&lt;/p&gt;</description></item><item><title>Lesson 3: encoding/json Beyond Basics — Custom marshalers, streaming, and the traps</title><link>/post/go/go-stdlib-json/</link><pubDate>Mon, 30 Sep 2024 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-json/</guid><description>&lt;p&gt;&lt;code&gt;encoding/json&lt;/code&gt; is one of the first packages every Go developer uses and one of the last they fully understand. The basic &lt;code&gt;json.Marshal&lt;/code&gt; / &lt;code&gt;json.Unmarshal&lt;/code&gt; API is approachable. But the package contains a whole layer of capabilities — custom marshaling, streaming decoders, &lt;code&gt;json.RawMessage&lt;/code&gt; for deferred parsing, interface-type fields, and a surprising number of edge cases — that separate the code that works in demos from the code that works in production with adversarial inputs.&lt;/p&gt;</description></item><item><title>Lesson 7: std::process — Running external commands</title><link>/post/rust/rust-stdlib-process/</link><pubDate>Sun, 29 Sep 2024 19:15:00 +0000</pubDate><guid>/post/rust/rust-stdlib-process/</guid><description>&lt;p&gt;I once wrote a deployment script in Bash that grew to 800 lines with nested conditionals, string interpolation bugs, and error handling that amounted to &amp;ldquo;hope for the best.&amp;rdquo; Rewrote it in Rust using &lt;code&gt;std::process::Command&lt;/code&gt; — same functionality, but with actual error handling and type safety. The number of failed deployments dropped to zero.&lt;/p&gt;
&lt;h2 id="command--the-builder"&gt;Command — The Builder&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;std::process::Command&lt;/code&gt; is a builder for spawning child processes. You construct the command, configure it, then either run it to completion or spawn it and interact with its I/O.&lt;/p&gt;</description></item><item><title>Lesson 6: std::net — TCP, UDP, sockets</title><link>/post/rust/rust-stdlib-net/</link><pubDate>Fri, 27 Sep 2024 09:40:00 +0000</pubDate><guid>/post/rust/rust-stdlib-net/</guid><description>&lt;p&gt;The first network program I ever wrote — a chat server in college — had a bug where it would block forever waiting for one client&amp;rsquo;s message while all the other clients hung. Classic single-threaded socket mistake. Rust&amp;rsquo;s &lt;code&gt;std::net&lt;/code&gt; module gives you the same low-level socket primitives, but the ownership system actually helps you avoid some of those pitfalls.&lt;/p&gt;
&lt;h2 id="tcp--the-reliable-one"&gt;TCP — The Reliable One&lt;/h2&gt;
&lt;p&gt;TCP gives you ordered, reliable byte streams. You connect, you send bytes, they arrive in order (or the connection dies trying). That&amp;rsquo;s the deal.&lt;/p&gt;</description></item><item><title>Lesson 5: std::fmt — Formatting internals</title><link>/post/rust/rust-stdlib-fmt/</link><pubDate>Tue, 24 Sep 2024 11:05:00 +0000</pubDate><guid>/post/rust/rust-stdlib-fmt/</guid><description>&lt;p&gt;I once shipped a monitoring dashboard where all the latency values showed up as &amp;ldquo;Duration { secs: 0, nanos: 234000000 }&amp;rdquo; because I&amp;rsquo;d used &lt;code&gt;{:?}&lt;/code&gt; instead of implementing &lt;code&gt;Display&lt;/code&gt;. That&amp;rsquo;s the kind of thing that makes you sit down and actually learn how formatting works.&lt;/p&gt;
&lt;h2 id="display-vs-debug"&gt;Display vs. Debug&lt;/h2&gt;
&lt;p&gt;These two traits are the foundation of everything in &lt;code&gt;std::fmt&lt;/code&gt;. Every time you use &lt;code&gt;println!&lt;/code&gt;, &lt;code&gt;format!&lt;/code&gt;, or &lt;code&gt;write!&lt;/code&gt;, you&amp;rsquo;re invoking one of them.&lt;/p&gt;</description></item><item><title>Lesson 4: std::fs and std::path — Filesystem operations done right</title><link>/post/rust/rust-stdlib-fs/</link><pubDate>Sat, 21 Sep 2024 16:10:00 +0000</pubDate><guid>/post/rust/rust-stdlib-fs/</guid><description>&lt;p&gt;A colleague once deployed a script that used string concatenation for file paths: &lt;code&gt;dir + &amp;quot;/&amp;quot; + filename&lt;/code&gt;. Worked perfectly on Linux, blew up on Windows, and silently corrupted paths when &lt;code&gt;dir&lt;/code&gt; ended with a slash. Rust&amp;rsquo;s &lt;code&gt;Path&lt;/code&gt; type exists specifically to prevent this class of bug.&lt;/p&gt;
&lt;h2 id="path-vs-pathbuf--the-strstring-split"&gt;Path vs. PathBuf — The &amp;amp;str/String Split&lt;/h2&gt;
&lt;p&gt;Just like Rust has &lt;code&gt;&amp;amp;str&lt;/code&gt; (borrowed) and &lt;code&gt;String&lt;/code&gt; (owned), it has &lt;code&gt;&amp;amp;Path&lt;/code&gt; (borrowed) and &lt;code&gt;PathBuf&lt;/code&gt; (owned). The duality is identical:&lt;/p&gt;</description></item><item><title>Lesson 3: std::io — Read, Write, BufRead, Seek</title><link>/post/rust/rust-stdlib-io/</link><pubDate>Thu, 19 Sep 2024 08:30:00 +0000</pubDate><guid>/post/rust/rust-stdlib-io/</guid><description>&lt;p&gt;Early in my Rust journey, I wrote a log parser that read a 2GB file byte by byte using &lt;code&gt;read()&lt;/code&gt; without buffering. It took forty minutes. Adding a &lt;code&gt;BufReader&lt;/code&gt; wrapper — one line of code — brought it down to three seconds. That&amp;rsquo;s when I learned that understanding &lt;code&gt;std::io&lt;/code&gt; isn&amp;rsquo;t optional.&lt;/p&gt;
&lt;h2 id="the-four-core-traits"&gt;The Four Core Traits&lt;/h2&gt;
&lt;p&gt;Rust&amp;rsquo;s I/O system is built on four traits. Everything — files, network sockets, stdin, pipes, in-memory buffers — implements some combination of these:&lt;/p&gt;</description></item><item><title>Lesson 2: Iterator Trait and Adapters — The full picture</title><link>/post/rust/rust-stdlib-iterators/</link><pubDate>Tue, 17 Sep 2024 14:45:00 +0000</pubDate><guid>/post/rust/rust-stdlib-iterators/</guid><description>&lt;p&gt;The moment Rust iterators clicked for me was when I realized they&amp;rsquo;re not loops with extra steps — they&amp;rsquo;re a completely different way of expressing data transformations. I&amp;rsquo;d been writing imperative loops for fifteen years, and the first time I refactored a gnarly nested-loop function into an iterator chain, the result was half the lines and twice as readable.&lt;/p&gt;
&lt;h2 id="the-iterator-trait"&gt;The Iterator Trait&lt;/h2&gt;
&lt;p&gt;Everything starts here. The &lt;code&gt;Iterator&lt;/code&gt; trait is shockingly simple:&lt;/p&gt;</description></item><item><title>Lesson 1: Collections Deep Dive — Vec, VecDeque, BTreeMap and when to use which</title><link>/post/rust/rust-stdlib-collections/</link><pubDate>Sun, 15 Sep 2024 10:22:00 +0000</pubDate><guid>/post/rust/rust-stdlib-collections/</guid><description>&lt;p&gt;I spent two days debugging a performance cliff in a data pipeline — turns out I&amp;rsquo;d been using a &lt;code&gt;HashMap&lt;/code&gt; where a &lt;code&gt;BTreeMap&lt;/code&gt; would&amp;rsquo;ve cut iteration time in half because I needed sorted output downstream. The collection you pick matters more than most people think.&lt;/p&gt;
&lt;h2 id="the-big-picture"&gt;The Big Picture&lt;/h2&gt;
&lt;p&gt;Rust&amp;rsquo;s standard library ships a small but deliberate set of collections. Unlike languages that give you seventeen flavors of list, Rust gives you a few well-designed options and expects you to understand the trade-offs. That&amp;rsquo;s actually a feature — fewer choices means you can develop genuine intuition about when to use what.&lt;/p&gt;</description></item><item><title>Lesson 2: io Patterns — Reader, Writer, and the composability that makes Go great</title><link>/post/go/go-stdlib-io/</link><pubDate>Fri, 16 Aug 2024 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-io/</guid><description>&lt;p&gt;The &lt;code&gt;io&lt;/code&gt; package is three hundred lines of interface definitions and a handful of utility functions. It is also the spine of the entire Go standard library. Every package that reads or writes data — &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;net&lt;/code&gt;, &lt;code&gt;compress/gzip&lt;/code&gt;, &lt;code&gt;crypto/tls&lt;/code&gt;, &lt;code&gt;encoding/json&lt;/code&gt;, &lt;code&gt;bufio&lt;/code&gt; — does it through &lt;code&gt;io.Reader&lt;/code&gt; and &lt;code&gt;io.Writer&lt;/code&gt;. Once you understand these two interfaces, the standard library clicks into place as one coherent system.&lt;/p&gt;
&lt;p&gt;I spent my first few months with Go treating &lt;code&gt;io.Reader&lt;/code&gt; as the thing that HTTP response bodies happen to be. It wasn&amp;rsquo;t until I built a streaming CSV processor — piping data from S3 through gzip decompression into a CSV parser, all without loading the file into memory — that I understood what the interfaces were actually designed for.&lt;/p&gt;</description></item><item><title>Lesson 1: net/http Deep Dive — The most important package you use wrong</title><link>/post/go/go-stdlib-net-http/</link><pubDate>Tue, 02 Jul 2024 00:00:00 +0000</pubDate><guid>/post/go/go-stdlib-net-http/</guid><description>&lt;p&gt;Every Go web service I&amp;rsquo;ve reviewed starts with &lt;code&gt;net/http&lt;/code&gt;. Most of them use it correctly for the obvious parts and incorrectly for the subtle ones. The package API is deceptively simple — &lt;code&gt;http.HandleFunc&lt;/code&gt;, &lt;code&gt;http.ListenAndServe&lt;/code&gt;, and you&amp;rsquo;re serving HTTP. But the design decisions underneath — how request routing works, what a &lt;code&gt;Handler&lt;/code&gt; actually is, how the server manages connections, what the default timeouts are — contain enough traps to keep a senior engineer busy for a week.&lt;/p&gt;</description></item></channel></rss>