Exceptions are an anti-pattern. If a function can encounter an error, it should expose this fact through its interface (specifically its return type).<p>For example, Rust has a Result type [1], so this function would return Result<Item, ItemNotFound>. This forces the caller to think about what to do in case of an error and specify what should happen.<p>[1] <a href="https://doc.rust-lang.org/std/result/index.html" rel="nofollow">https://doc.rust-lang.org/std/result/index.html</a>
Just a small suggestion to consider: If the title was "Zero-Cost Exceptions and Error Interfaces in Python" I would have received it in my personal Hacker News feed in Telegram (<a href="https://github.com/lawxls/HackerNews-personalized" rel="nofollow">https://github.com/lawxls/HackerNews-personalized</a>) as I do have "Python" keyword added. And there's also email notification services people use which might have Python as a keyword.<p>Thanks for providing further resources btw, added Sebastian Witowski presentation to "watch later" list :)
As far as perf goes, a callee raising an exception is never the answer.<p>The perf arguments are a sham imo.<p>A callee cannot know how many times it is going to be called, only a caller, or a caller's caller, etc, can know that.
I like how the closest thing to a "correct" answer gets dismissed almost immediately with fairly weak reasoning.<p>You're adding type hints so they're not going to be blind sided anyways, just return a union of Item and None. If None is a valid value then use a result type.<p>Exceptions are for exceptional cases isn't just a performance thing, it's a reasoning thing: You're jumping out of the established control flow and there should be a very good reason for that.<p>If you're struggling with handling None even when you know it's a valid return value, living in a codebase that overuses exceptions is going to be 10x worse.