Interface

Trait Interface 

pub trait Interface<Input, Output> {
    // Required method
    fn call(input: Input) -> Output;
}
Expand description

An Interface specifies the behavior of a system as a map from input to output.

It is easy to think of an interface as a function. But it is far more general and can represent a function, a network call, an event, a process, an event or a complete system. The essence of Interface is captured by the call method.

  fn call(input: I) -> O;

§A Few Examples

§A Function as Interface

TBD

§A Type implementing Interface

A simple increment interface. This is not very interesting, but just to start building familiarity with Interfaces.

use metals_poly::interface::Interface;
/// The types that will implement the necessary increment behavior
struct Increment;

/// Increment represents a computation that turns i32 into some other i32.
impl Interface<i32, i32> for Increment {
   /// Increment the input by 1.
   fn call(input: i32) -> i32 {
      input + 1
    }
}

assert_eq!(Increment::call(1), 2);

There are no self (read it as no state) or multiple arguments or anything interesting here at all. Looks very boring, to be honest. While it may not appear terribly exciting, let us build a few more interfaces for Increment.

struct Increment;
// x-- hiding previous lines for brevity

// Increment interface for u32 -> u32
impl Interface<u32, u32> for Increment {
   fn call(input: u32) -> u32 {
      input + 1
    }
}

// We have to tell the input type
assert_eq!(Increment::call(1u32), 2u32);
// This calls previous i32 -> i32
assert_eq!(Increment::call(1), 2);

But if we add another interface say, say to increment by 10, for same input and output types, we get compiler error.

struct Increment;

impl Interface<i32, i32> for Increment {
   fn call(input: i32) -> i32 {
      input + 1
    }
}

// This will lead to compiler error for reimplementing the same function
impl Interface<i32, i32> for Increment {
   fn call(input: i32) -> i32 {
      input + 10
    }
}

But we can mix input and output types. For e.g., we can have the same increment by 1 method defined for i32 -> u32 (or vice versa).

struct Increment;
// x-- hiding previous code lines for brevity
// Increment interface for i32 -> i32
impl Interface<i32, i32> for Increment {
// x-- snip implementation code
}
// Increment interface for u32 -> u32
impl Interface<u32, u32> for Increment {
// x-- snip implementation code
}
// Increment interface for i32 -> u32
impl Interface<i32, u32> for Increment {
   fn call(input: i32) -> u32 {
// x-- snip implementation code
      u32::try_from(input).unwrap() + 1u32
    }
}

// Now we have to provide a lot of type information to both at calling
// and at assert

// Calling i32 -> i32. Needs type info even for i32 -> i32
let o: i32 = Increment::call(1i32);
assert_eq!(o, 2i32);
// Calling i32 -> u32
let o: u32 = Increment::call(1i32);
assert_eq!(o, 2u32);
// Calling u32 -> u32, not different from last time
assert_eq!(Increment::call(1u32), 2u32);

Required Methods§

fn call(input: Input) -> Output

An interface is called with an input.

Please note that we are passing a mutable reference to self which is an indication that the internal state may be muted.

§Example

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§