{
  "ogp": {
    "url": "https://oyster.us-east.host.bsky.network/xrpc/com.atproto.sync.getBlob?did=did%3Aplc%3Ac6te24qg5hx54qgegqylpqkx&cid=bafkreic2oadqcsd3c6zhyspggjw6qlb5at527m2loxtfzjinzjewwrekwa"
  },
  "$type": "com.whtwnd.blog.entry",
  "blobs": [
    {
      "name": "hello world try fromresidual card image.png",
      "blobref": {
        "$type": "blob",
        "ref": {
          "$link": "bafkreic2oadqcsd3c6zhyspggjw6qlb5at527m2loxtfzjinzjewwrekwa"
        },
        "mimeType": "image/png",
        "size": 56894
      },
      "encoding": "image/png"
    }
  ],
  "theme": "github-light",
  "title": "HELLO, WORLD? (Abusing Try&FromResidual in Rust)",
  "content": "So, I've been wanting to start with blogging for a while now. I always stopped short of writing a single word due to exhaustion, feeling like I had to find something more interesting, that it wasn't \"the right first blog post\"... et cetera.\n\nAnyway, today I found myself in the mood to mess around with Rust again; specifically, new fun ways to write cursed syntax. Eventually, I landed on the `?` operator, and the experimental `try_trait_v2`. In brief, this is a set of traits, which allow implementors to make use of the `?` operator for more convenient error checking. Without further ado, here's a snippet of the cursed code that I came up with:\n\n*Edit: I managed to refine the code a little - it no longer needs a `try { }` block, and has some other improvements. See [Edit 1](#user-content-edit-1).*\n\n```rust\n// [Implementation code above...]\n\nconst S: TryPrinter = TryPrinter(AsciiChar::Space);\n\nfn main() {\n    TryPrinter(_) = try {\n        S????????????????????????????????????????;\n        S?????????????????????????????????????;\n        S????????????????????????????????????????????;\n        S????????????????????????????????????????????;\n        S???????????????????????????????????????????????;\n        S????????????;\n        S;\n        S???????????????????????????????????????????????????????;\n        S???????????????????????????????????????????????;\n        S??????????????????????????????????????????????????;\n        S????????????????????????????????????????????;\n        S????????????????????????????????????;\n        S???????????????????????????????\n    };\n}\n```\n\nYou may *try* to guess what this code does... Really, it's nothing too complicated, and there are a lot of hints everywhere anyway.\n<details>\n<summary>Show</summary>\nIt's a simple Hello World - or, rather, <code>HELLO, WORLD?</code>\n</details>\n\n[**Playground link (v2)**][playground link v2]\n\n[*Old playground link*][Playground link]\n\n---\n\nHere are the implementation details:\n\nFirstly, there's `try_trait_v2`. This unlocks the `Try` and `FromResidual` traits, allowing you to implement them on your types to make them usable with the `?` operator.\n\n```rust\n#![feature(try_trait_v2)]\n#![feature(ascii_char)]\nuse std::ascii::Char as AsciiChar;\nuse std::ops::Try;\n\nstruct TryPrinter(AsciiChar);\n\nimpl FromResidual for TryPrinter {\n    // ...\n}\n\nimpl Try for TryPrinter {\n    // ...\n}\n```\n\n`?` is Rust's early-return operator, often a convenient shorthand for the equivalent of a null check, but it also propagates errors from `Result<T, E>`. While you may think of them as a simple `match` on the option or result with a `return` in the `None` or `Err` cases, the actual logic is a bit more complex. Importantly, the value is sent through `Try::branch(self)` first, which decides whether to continue, or break out early.\n\n```rust\nimpl Try for TryPrinter {\n    type Output = TryPrinter;\n    type Residual = ();\n\n    // ...\n\n    fn branch(mut self) -> ControlFlow<Self::Residual, Self::Output> {\n        let c = self.0;\n        match AsciiChar::from_u8(c.to_u8() + 1) {\n            None => ControlFlow::Break(()),\n            Some(x) => {\n                self.0 = x;\n                ControlFlow::Continue(self)\n            }\n        }\n    }\n}\n```\n\n`Try` is defined in terms of two associated types: `Output` and `Residual`. *Commonly*, `Output` is the equivalent of a successful unwrap, and `Residual` is like a failed null check or a propagated error - however, your implementation may do anything else instead. My `TryPrinter` type simply stores an `ascii::Char` (a newtyped `u8`), and the `Try` implementation on it does one of two things: it returns the `TryPrinter` with the character incremented, but if it would overflow, it instead returns a `Residual` via `ControlFlow::Break` (in this case, I just made it return `()`).\n\n*[Edit 1](#user-content-edit-1): I split `TryPrinter` into a generic `IterOnTry` and a (non-generic) `AsciiCharDropPrinter`. See [below](#user-content-edit-1) for details.*\n\nNow, chaining `?` operators is completely valid syntax already. Weird, maybe, but `Option<Option<T>>` can absolutely accept `??` as a double-unwrap[^flatten]. My attempt to abuse this led to a type whose `Try::Output` was simply itself, which allowed for infinite `?` chaining. It's no fun when it's all no-ops, so I instead made it store a value, which gets incremented by every subsequent `?`.\n\nThe second part of the secret sauce is my favorite trick, `print!` on `drop`. Each line in the sample code starts with the `S` constant[^const-with-drop], increments its char value repeatedly, and drops the struct at the end of the statement, printing the stored character to the screen. *[Edit 1](#user-content-edit-1): again, the types have been shuffled around slightly, see [below](#user-content-edit-1) for details.*\n\n```rust\nimpl Drop for TryPrinter {\n    fn drop(&mut self) {\n        print!(\"{}\", self.0);\n    }\n}\n```\n\nFinally, some extra fluff around the code is needed to make it all work:\n- Implementors of `Try` also need to implement `FromResidual` - I just made it return something and didn't bother with it all that much[^from-residual].\n  - *[Edit 1](#user-content-edit-1): the Residual type is now `Option<Infallible>`*\n- `?` may only be used in functions that return an `Option` or a `Result`. Rather than making a wrapper function, I used the (also experimental) `try { }` block.\n  - The `try { }` block needs help with type inference in this case, so the block is assigned to a pattern (and the last expression has no semicolon).\n  - *[Edit 1](#user-content-edit-1): `try { }` is no longer needed*\n- I used the `S` constant so I could start with space (0x20) rather than NUL (0x00), because I wanted the question marks to fit on the screen.\n\nCheck out the [Playground link] for the full source code. *[Edit 1](#user-content-edit-1): [Playground link v2]*\n\nOne last note: I'll see if I can get rid of the `try { }` block by implementing `std::process::Termination` somehow to let me use `?` within `main` itself. Something about the type inference requirement also doesn't quite feel right, and might be related to my messy implementation of `FromResidual`, but I'll need to understand the traits better to solve that. *See [Edit 1](#user-content-edit-1).*\n\n---\n\nI've been wanting to start with blogging for a while now. I always stopped short of writing a single word for various reasons, but I guess the only way to get it done is to simply `try`. I love messing around with Rust in all sorts of ways, and this experiment lent itself well to a nice little blog writeup.\n\nAnd what better way to start with blogging than with a `HELLO, WORLD?`\n\n---\n\n<h3 id=\"edit-1\">Edit 1</h3>\n\n**[Playground link (v2)][playground link v2]**\n\nI thought about it further and made some tweaks to the implementation:\n\nI split `TryPrinter` into a generic `IterOnTry<T>`, and a specific `AsciiCharDropPrinter`. `IterOnTry<T>` is now responsible for the `?` part of the mess - it holds an `Option`-wrapped `Iterator`, and calls `next()` on it each time `?` is applied. If the iterator runs out, it stores a `None` instead. `AsciiCharDropPrinter` is now solely responsible for incrementing its `ascii::Char`, and implementing `Drop`-printing - generic types are not allowed to implement `Drop` due to some unsoundness issues that could cause.\n\nThis made the whole implementation more generic and extensible.\n\n`Residual` is `Option<Infallible>` now instead, which also makes it compatible with `Option`-returning functions (due to `Option<T>: FromResidual<Option<Infallible>>`). By extension, `IterOnTry<T> : FromResidual<Option<Infallible>>` as well, which simply yields an `IterOnTry(None)` - pretty much the same as the `Option<T>` impl.\n\nThis made the `Residual` type more proper/meaningful, as well as compatible with existing implementations.\n\nFinally, by implementing `std::process::Termination` on `IterOnTry<T>`, `?` can be used directly in `main()` (assuming `main()` returns some sort of `IterOnTry<_>`).\n\nThis removes the need for a `try { }` wrapper (and related type inference issues).\n\nI also cleaned up some redundant impls, dropped `!` in favor of `Infallible` (one less feature gate, compat with existing impls), and added some comments. More importantly, though, I think I have a better understanding of the traits now as well!\n\n[^flatten]: Though, you might want to use `Option::flatten(self)` instead; ditto for `Result::flatten(self)` once it gets stabilized.\n\n[^const-with-drop]: `const` items *may* implement `Drop`! Every time you refer to the constant, a new instance is constructed at that location, and that gets dropped as usual. The `const` item itself, though, doesn't get dropped after `main` or anything like that.\n\n[^from-residual]: My rough understanding is: `Try::Residual` is a \"definitely-error\" version of the main type, and `FromResidual` re-pairs it with an output type again. This means you can go from `Result<T, E>` through `Result<Infallible, E>` to `Result<U, E>` without needing `T` and `U` to be the same type. In any case, given how I implemented my `TryPrinter` struct here, I didn't bother with figuring out a proper error/residual type. Note: see also [Edit 1](#user-content-edit-1)\n\n[playground link]: <https://play.rust-lang.org/?version=nightly&mode=release&edition=2024&gist=88caf53624848d551ca2776d0eeb8f76> (Playground v1)\n[playground link v2]: <https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=6ece27cf25d605c590cfb1dd5294ba22> (Playground v2)",
  "createdAt": "2025-06-01T14:24:37.502Z",
  "visibility": "public"
}