Giter Site home page Giter Site logo

Comments (8)

alexcrichton avatar alexcrichton commented on June 12, 2024

Thanks for the question, and this is indeed possible! You can create new futures with combinators with functions like map and then which run more computations once a future has completed.

In this way you can "attach callbacks" by transforming the underlying future. It's a little different from exactly what you'll see in Guava and Akka I believe, but it all achieves the same goal!

Let me know if that's not what you were thinking, though.

from futures-rs.

norru avatar norru commented on June 12, 2024

Unfortunately not.

Attaching a callback is not the same as composing futures.

Thanks anyway, will have to look at some other lib.

from futures-rs.

ishbir avatar ishbir commented on June 12, 2024

Attaching a callback is not the same as composing futures.

What's the difference that makes this crate a deal-breaker? I'm curious.

from futures-rs.

alexcrichton avatar alexcrichton commented on June 12, 2024

@norru, to be clear, you should be able to do something like:

fn on_complete<A, F>(a: A, f: F) -> Box<Future<Item=A::Item, Error=A::Error>>
    where A: Future,
          F: FnOnce(&A::Item) + 'static,
{
    Box::new(a.map(|x| {
        f(&x);
        x
    }))
}

That way you can register "completion callbacks" and similarly error callbacks if necessary.

from futures-rs.

norru avatar norru commented on June 12, 2024

(deleted comment, Gmail broke my Markdown, see below)

from futures-rs.

norru avatar norru commented on June 12, 2024

Fair enough! It looks like the discussion is far from over and I might have sounded a bit too dismissive to begin with. Apologies for that! :)

TL;DR this pattern built on ".map()" doesn't work for me but maybe we're getting somewhere.

I haven't tried yet but there may be a limitation to what I want to do WRT "fire and forget" as Rust's scoping rules may forbid patterns I'm used to in other languages/environments anyway so basically I'm not bound by the library but by the language itself. I need to try that as my Rust-fu is not that good. For instance, if I do:

let c = on_complete(...)

What happens when c goes out of scope?

Anyway, here's a bunch of things that I would expect when registering a callback, as I'm kind of used to:

  • Callbacks are only be used for side effects - typically sending "fire or forget" messages or, say, logging.
  • Callbacks cannot cause the future to fail or alter its result
  • Multiple callbacks can be attached independently to the same future. If I pass a future to multiple subsystems, even concurrently, each system can register its own interest to the result independently.
  • Order of execution of callbacks doesn't matter in the general case (but I've used mechanisms that make it possible)

Here's a completely made up example of a thing I'm expecting to be able to do. Excuse the "pseudo-Rust".

fn main() {
  a  = <some future>
  ...
  b = a.map(|x| { ... })
  ...
  do_all(a, b)

  ...keep doing stuff...

}
...

fn do_all(a, b) {
  ...
  proc1(a, b)
  proc2(a, b)
  ...
}

fn proc1(a, b) {
  ...
  on_callback(a, |x| { ..write x to file "A" })
  ...
}

fn proc2(a, b) {
  ...
  on_callback(b, |x| { ..send message x...})
  ...
}

This allows main to fire some computations, one depending on the other, and pass both results down the line without having to care about what is interested in what, and without the two subsystems caring whether a or b are dependent from each other.

If you were to ask "why would you want to do that" the answer would be "because". I already do that somewhere else: it works, it's efficient, it's easy, it's compact, it's maintainable, and it's "reasonably safe" in the right context. But, again, I may be missing something related to the Rust-ness of the thing and I wouldn't be looking at Rust if I were 100% happy with what I've got.

from futures-rs.

alexcrichton avatar alexcrichton commented on June 12, 2024

One part about this crate is that it's intended to be maximally flexible not necessarily locking you into any particular paradigm or pattern. That's done through having futures as a trait rather than a type. That way you can implement whatever semantics you'd like.

With that in mind it's also worth pointing out that today while there's no exact analog for "attach a list of callbacks to get run on completion" it can certainly be done with a custom trait implementation! So while this can be emulated via map, you could also imagine a custom adapter which implements this paradigm.

Finally, ownership in Rust means that many exact patterns in other languages don't translate well. For example a future with this library can never be concurrently referenced because all the methods take &mut self. A handle could be concurrently referenced, which again the trait allows for, but a future itself wouldn't be. Additionally by tracking ownership of a future we're able to precisely know who's got access to that and track it over time as well.

For example in the snippet you gave this would be illegal in Rust:

proc1(a, b)
proc2(a, b)

as that would move a and b with the first procedure, disallowing access with the next. You could pass around &mut a and &mut b, however, and use a custom adapter to register callbacks, though, and it'd solve your problem I believe.

Does that all make sense? Would be more than willing to clarify anything if needed!

from futures-rs.

norru avatar norru commented on June 12, 2024

Yep, that's illegal, but, as I clearly stated, it's not Rust either.

I just didn't want to start down the rabbit hole of borrowage so I went through the easier "pseudo-Rust" way to explain what my intent was.

I can also imagine lots of adapters and also implementing my own futures - but that is the kind of common things that I would expect a good Futures library to solve for me, so I don't have to reinvent the wheel. Attaching a callback is a very common pattern.

Any sufficiently flexible library is indistinguishable from no library at all ;)

By the way, in the other Futures libraries that I know and use (caveat: not on Rust!), callbacks are a powerful primitive on which I build my own abstractions. I do also use composition with great effects (or at least get the job done). I know of libraries which don't implement callbacks as primitives (on which you can bolt-on a half-assed implementation of those if you hammer them hard enough) and that's a reason good enough to avoid them altogether.

from futures-rs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.