[][src]Trait dialectic::NewSession

pub trait NewSession where
    Self: Actionable,
    Self::Dual: Actionable
{ pub fn channel<Tx, Rx>(
        make: impl FnMut() -> (Tx, Rx)
    ) -> (Chan<Tx, Rx, Self>, Chan<Tx, Rx, Self::Dual>)
    where
        Tx: Send + 'static,
        Rx: Send + 'static
;
pub fn bichannel<Tx0, Rx0, Tx1, Rx1>(
        make0: impl FnOnce() -> (Tx0, Rx0),
        make1: impl FnOnce() -> (Tx1, Rx1)
    ) -> (Chan<Tx0, Rx1, Self>, Chan<Tx1, Rx0, Self::Dual>)
    where
        Tx0: Send + 'static,
        Rx0: Send + 'static,
        Tx1: Send + 'static,
        Rx1: Send + 'static
;
pub fn wrap<Tx, Rx>(tx: Tx, rx: Rx) -> Chan<Tx, Rx, Self>
    where
        Tx: Send + 'static,
        Rx: Send + 'static
;
pub fn over<Tx, Rx, T, Err, F, Fut>(
        tx: Tx,
        rx: Rx,
        with_chan: F
    ) -> Over<Tx, Rx, T, Err, Fut>

Notable traits for Over<Tx, Rx, T, E, Fut>

impl<Tx, Rx, T, E, Fut> Future for Over<Tx, Rx, T, E, Fut> where
    Fut: Future<Output = Result<T, E>>, 
type Output = Result<(T, Result<(Tx, Rx), SessionIncomplete<Tx, Rx>>), E>;

    where
        Tx: Send + 'static,
        Rx: Send + 'static,
        F: FnOnce(Chan<Tx, Rx, Self>) -> Fut,
        Fut: Future<Output = Result<T, Err>>
; }

The NewSession extension trait gives methods to create session-typed channels from session types. These are implemented as static methods on the session type itself.

Examples

use dialectic::prelude::*;
use dialectic::backend::mpsc;

let (c1, c2) = <Send<String>>::channel(mpsc::unbounded_channel);
// do something with these channels...

Notes

The trait bounds specified for NewSession ensure that the session type is well-formed. However, it does not ensure that all the types in the session type can be sent or received over the given channels.

Valid session types must contain only "productive" recursion: they must not contain any Continue directly inside the loop to which that recursion refers. For instance, attempting to use the session type Loop<Continue> will result in a compile-time trait solver overflow.

use dialectic::*;
use dialectic::backend::mpsc;

let (c1, c2) = <Loop<Continue>>::channel(mpsc::unbounded_channel);

This results in the compiler error:

error[E0275]: overflow evaluating the requirement `Continue: Actionable<(Continue, ())>`
  |
7 | let (c1, c2) = <Loop<Continue>>::channel(backend::mpsc::unbounded_channel);
  |                ^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: consider adding a `#![recursion_limit="256"]` attribute to your crate
  = note: required because of the requirements on the impl of `Actionable<()>` for `Loop<Continue>`
  = note: required because of the requirements on the impl of `NewSession` for `Loop<Continue>`

In this situation, you should not take rustc's advice. If you add a #![recursion_limit="256"] attribute to your crate: this will only cause the compiler to work harder before giving you the same error!

What the compiler is trying to tell you is that the session type as specified does not correspond to a valid sequence of actions on the channel, because it contains unproductive recursion.

Required methods

pub fn channel<Tx, Rx>(
    make: impl FnMut() -> (Tx, Rx)
) -> (Chan<Tx, Rx, Self>, Chan<Tx, Rx, Self::Dual>) where
    Tx: Send + 'static,
    Rx: Send + 'static, 
[src]

Given a closure which generates a uni-directional underlying transport channel, create a pair of dual Chans which communicate over the transport channels resulting from these closures.

By internally wiring together the two directional channels, this function assures that communications over the channels actually follow the session specified.

Examples

use dialectic::prelude::*;
use dialectic::backend::mpsc;

let (c1, c2) = Done::channel(mpsc::unbounded_channel);

pub fn bichannel<Tx0, Rx0, Tx1, Rx1>(
    make0: impl FnOnce() -> (Tx0, Rx0),
    make1: impl FnOnce() -> (Tx1, Rx1)
) -> (Chan<Tx0, Rx1, Self>, Chan<Tx1, Rx0, Self::Dual>) where
    Tx0: Send + 'static,
    Rx0: Send + 'static,
    Tx1: Send + 'static,
    Rx1: Send + 'static, 
[src]

Given two closures, each of which generates a uni-directional underlying transport channel, create a pair of dual Chans which communicate over the transport channels resulting from these closures.

By internally wiring together the two directional channels, this function assures that communications over the channels actually follow the session specified.

Examples

use dialectic::prelude::*;
use dialectic::backend::mpsc;

let (c1, c2) = Done::bichannel(
    mpsc::unbounded_channel,
    || mpsc::channel(1),
);

pub fn wrap<Tx, Rx>(tx: Tx, rx: Rx) -> Chan<Tx, Rx, Self> where
    Tx: Send + 'static,
    Rx: Send + 'static, 
[src]

Given a transmitting and receiving end of an un-session-typed connection, wrap them in a new session-typed channel for the protocol P.

It is expected that the other ends of these connections will be wrapped in a channel with the Dual session type.

Examples

use dialectic::prelude::*;
use dialectic::backend::mpsc;

let (tx, rx) = mpsc::unbounded_channel();
let c = Done::wrap(tx, rx);
c.close();

pub fn over<Tx, Rx, T, Err, F, Fut>(
    tx: Tx,
    rx: Rx,
    with_chan: F
) -> Over<Tx, Rx, T, Err, Fut>

Notable traits for Over<Tx, Rx, T, E, Fut>

impl<Tx, Rx, T, E, Fut> Future for Over<Tx, Rx, T, E, Fut> where
    Fut: Future<Output = Result<T, E>>, 
type Output = Result<(T, Result<(Tx, Rx), SessionIncomplete<Tx, Rx>>), E>;
where
    Tx: Send + 'static,
    Rx: Send + 'static,
    F: FnOnce(Chan<Tx, Rx, Self>) -> Fut,
    Fut: Future<Output = Result<T, Err>>, 
[src]

Given a closure which runs the session on a channel from start to completion, run that session on the given pair of sending and receiving connections.

Errors

The closure must finish the session P on the channel given to it and drop the finished channel before the future returns. If the channel is dropped before completing P or is not dropped after completing P, a SessionIncomplete error will be returned instead of a channel for Q. The best way to ensure this error does not occur is to call close on the channel before returning from the future, because this statically checks that the session is complete and drops the channel.

Examples

use dialectic::prelude::*;
use dialectic::backend::mpsc;

let (tx, rx) = mpsc::unbounded_channel();
let (output, ends) = Done::over(tx, rx, |chan| async move {
    chan.close();
    Ok::<_, mpsc::Error>("Hello!".to_string())
}).await?;

assert_eq!(output, "Hello!");
let (tx, rx) = ends?;

As noted above, an error is thrown when the channel is dropped too early:

use SessionIncomplete::BothHalves;
use IncompleteHalf::Unfinished;

let (tx, rx) = mpsc::unbounded_channel();
let (_, ends) = <Send<String>>::over(tx, rx, |chan| async move {
    Ok::<_, mpsc::Error>(())
}).await?;

assert!(matches!(ends, Err(BothHalves { tx: Unfinished(_), rx: Unfinished(_) })));

And likewise, an error is thrown when the channel is not dropped by the end of the future:

use std::sync::{Arc, Mutex};

use SessionIncomplete::BothHalves;
use IncompleteHalf::Unclosed;

// We'll put the `Chan` here so it outlives the closure. **Don't do this!**
let hold_on_to_chan = Arc::new(Mutex::new(None));
let hold = hold_on_to_chan.clone();

let (tx, rx) = mpsc::unbounded_channel();
let (_, ends) = <Send<String>>::over(tx, rx, |chan| async move {
    *hold.lock().unwrap() = Some(chan);
    Ok::<_, mpsc::Error>(())
}).await?;

assert!(matches!(ends, Err(BothHalves { tx: Unclosed, rx: Unclosed })));

// Make sure the `Chan` outlives the closure. **Don't do this!**
drop(hold_on_to_chan);
Loading content...

Implementors

impl<P> NewSession for P where
    Self: Actionable,
    Self::Dual: Actionable
[src]

Loading content...