Hacker News new | past | comments | ask | show | jobs | submit login

>every function is throwing

It was a poor choice of words. You can replace 'throwing functions' with 'throwing code'. There is plenty of code that is guaranteed not to throw -- starting from operations on basic types and ending with noexcept functions if we are talking about C++.

>The compiler won't warn you when this happens.

It will if you write an asserting in the code that relies on a function not throwing:

  static_assert(noexcept(f()));
>`f` can be generic over the error type

Isn't it too much clutter? What if a function has three arguments? You'd need three generic parameters for their errors.




> Isn't it too much clutter?

I have never seen, other than like, methods on Result itself, Rust code that accepts a Result type as an argument. You are absolutely right that would lead to a crazy amount of clutter.

> Imagine you need to call f(h1()) and f(h2()) where h1() and h2() return the same data type for valid data but different types of errors. What do you do?

It sort of depends on the specific different error types. Most of the code I've been writing is application level code, so I'd just be returning the errors upstream. The anyhow library makes it easy to return a "trait object" which is sort of like a virtual base class in C++, at least for these purposes. With that I could just do

    f(h1()?);
    f(h2()?);
The ? operator lets you return the error half of the Result to the parent. If I had to do the conversions myself, if I implemented some traits, I could do this too. That said, I would probably use a temporary for clarify:

    let x = h1()?;
    f(x);

    let x = h2()?;
    f(x);
For some cases, I may not want to implement the traits, but maybe there's some sort of common error that I'd want to return to the caller. In that case, I may

    let x = h1().map_err(|e| /* do whatever conversion here */ )?;
    f(x);

    let x = h2().map_err(|e| /* do whatever conversion here */ )?;
    f(x);
These three represent the vast majority of this kind of code I write and see in the wild.


Thanks for chiming in.

When I looked at anyhow, I remember thinking that it's just a way to emulate in Rust the kind of error propagation that C++ has with exceptions, but manually with ?.

What's your take on this?


I think that it's reasonable to look at two features designed to solve similar problems, and think they look similar. And yeah, anyhow feels closer to an exception. There's some differences though; you don't have to use anyhow, so in some senses, Rust's system here has more flexibility overall. Another is that anyhow gives you a more "semantic" backtrace by default. Here's what I mean: Consider this program:

    use anyhow::Result;
    use anyhow::bail;
    
    fn main() -> Result<()> {
        foo()?;
        
        Ok(())
    }
    
    fn foo() -> Result<()> {
        bar()?;
        
        Ok(())
    }
    
    fn bar() -> Result<()> {
        bail!("oh no");
    }
When you run this, you'll see

  Error: oh no
We don't get a stacktrace, we get our error.

Whenever you want to insert something into this backtrace, you can:

    use anyhow::bail;
    use anyhow::Context;
    use anyhow::Result;
    
    fn main() -> Result<()> {
        foo()?;
        
        Ok(())
    }
    
    fn foo() -> Result<()> {
        bar().context("couldn't bar")?;
        
        Ok(())
    }
    
    fn bar() -> Result<()> {
        bail!("oh no");
    }
Running this, you instead get

  Error: couldn't bar
  
  Caused by:
      oh no
And if we add some context to foo:

  Error: was trying to foo
  
  Caused by:
      0: couldn't bar
      1: oh no
I find this to be a lot nicer than a stacktrace. And if you want to get a stacktrace, you can do so by setting the standard environment variable.


Yep, semantic backtrace is very handy when you need the user to understand the error and fix input data.


> There is plenty of code that is guaranteed not to throw -- starting from operations on basic types

This is fair, although this places some mental load on the programmer because they need to be careful when dealing with generics / operator overloading or when extracting helper functions.

> noexcept functions if we are talking about C++

This is a good C++-specific guarantee. But it's probably outweighted by the C++-specific lack of guarantees. "Operations on basic types" can now invoke UB, if we are talking about C++.

> Isn't it too much clutter?

In cases with one generic parameter, like the ones I demonstrated, I think it looks ok. Normal generic code. Rust generics are only going to require less ceremony in the future, because the team keeps making progress on implied bounds, lifetime elision, 2024 lifetime capture rules.

> What if a function has three arguments? You'd need three generic parameters for their errors.

This really depends on the context and what the code actually does, and needs a concrete example to discuss. I don't remember seeing a real world example with three Result parameters, nevermind having generic but distinct errors in them.


>careful when dealing with generics / operator overloading

In C++, you can't overload operators for built-in types and template parameters are normally constrained:

  auto f(std::integral auto x) noexcept
  {
      return x+x;
  }
But yes, writing C++ templates that work with wide range of types takes some thinking and operator overloading adds to the mental load (or reduces it, when used sparingly and properly).

>"Operations on basic types" can now invoke UB

They always could. I don't want this conversation to become about Rust vs. C++, I'd gladly use Rust when where is appropriate task.

>needs a concrete example to discuss

Write a new post when you come up with a bunch of good examples)


Sure. Thanks for the conversation. If you're interested in the follow up, I have an RSS feed: https://home.expurple.me/posts/index.xml


Thanks! What do people use nowadays to follow rss? A telegram bot perhaps?


Honestly, no idea. I got into RSS about a year ago. I just use Thunderbird, which I already use for my mail


I'll see how this one works: https://t.me/FeedRiverBot




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: