shepmaster / snafu

Easily assign underlying errors into domain-specific errors while adding context
https://docs.rs/snafu/
Apache License 2.0
1.4k stars 60 forks source link

TryFutureExt conflicts with futures #371

Open bjchambers opened 1 year ago

bjchambers commented 1 year ago

I'd like to be able to use methods from both traits, but am unable to use both since they have the same name:

use futures::TryFutureExt;
use snafu::TryFutureExt;

It's possible to work around this:

use futures::TryFutureExt;
use snafu::TryFutureExt as SnafuTryFutureExt;

But this won't be suggested by rust-analyzer, etc. Ideally, it would be possible to use both simultaneously. It might be helpful to rename the trait in snafu (possibly providing an alias for backwards compatibility). Alternatively, I could just use await.context(...) (eg., use ResultExt instead of TryFutureExt), but that raises the question of why there needs to be a TryFutureExt at all.

8573 commented 1 year ago

(I'm just a user, not a maintainer!)

But this won't be suggested by rust-analyzer, etc.

This sounds to me like R-A's problem, not Snafu's. Using traits (or other items) that need to be renamed[^1] on import is something Rust users need to be prepared to do, so, if Rust users depend on R-A to do it for them, R-A needs to be prepared to do so.

[^1]: or de-named with use ... as _;

shepmaster commented 1 year ago

I agree that this isn't really something that libraries should work around. I recommend using the _ import, and that's what the snafu::prelude. Ideally, all of the SNAFU docs will use this form (please let me know if you find any that don't!).

Also, we've raised an issue with rust-analyzer to suggest importing a prelude when present, which would help here.

Alternatively, I could just use await.context(...) (eg., use ResultExt instead of TryFutureExt), but that raises the question of why there needs to be a TryFutureExt at all.

Absolutely! SNAFU's future combinators were added a long while ago — I think before async and await were added to the language. When writing async code, you need to be careful of when you place your .awaits so that you don't lose concurrency. For example:

let a = a.await.context(...);
let b = b.await.context(...); // b will happen *after* a

// vs

let a = a.context(...);
let b = b.context(...);
let (a, b) = join!(a, b); // a and b will happen concurrently 

// or without SNAFU's combinators

let a = async { a.await.context(...) };
let b = async { b.await.context(...) };
let (a, b) = join!(a, b); // a and b will happen concurrently