Trait Mapping To Its Concrete Implementation

Migo·2023년 11월 4일
0

Fluent Rust

목록 보기
15/27
post-thumbnail

Why I needed to implement this

I was working on implementing my own DI container.

I put my business logic and handlers in service layers that are talking against trait(or interface).

The consumer of such an abstraction was presumed to have control of the following:

  • what data the injected dependency will have
  • lifetime of the data(this is not quite true in Rust as it is VERY strict about lifetime)

Preferrably, that happens at composition root so the possible code change spot is contained within the SMALLEST area possible in the code.

So, my idea was to register concrete implementation of a certain trait and map it to trait itself.

Code example

pub trait TRegister<T: ?Sized, U>{
	fn resolve(&self) -> U;
}

This is to register a certain trait and make it enable to resolve the mapped implementation. Note that I use ?Sized as trait is not sized.

struct Container<T: ?Sized>(PhantomData<T>);

This is concrete struct that's aware of Trait you pass in and it will implement TRegister discussed above.

Example

Suppose you have the following traits:

trait TSpeak {
    fn speak(&self) {
        println!("speak speak speak!")
    }
}

trait TRead {
    fn read(&self) {
        println!("read read read!")
    }
}

And then structs that implement them:

struct ConcreteA;
impl TSpeak for ConcreteA {}

struct ConcreteB;
impl TRead for ConcreteB {}

And then, you register them and let container aware of that mapping;

impl TRegister<dyn TSpeak, ConcreteA> for Container<dyn TSpeak> {
    fn resolve(&self) -> ConcreteA {
        ConcreteA
    }
}

impl TRegister<dyn TRead, ConcreteB> for Container<dyn TRead> {
    fn resolve(&self) -> ConcreteB {
        ConcreteB
    }
}

Now, let's have fun!

fn speaker(s: impl TSpeak) {
    s.speak()
}

fn reader(r: impl TRead) {
    r.read()
}
fn main() {
    let container = Container::<dyn TSpeak>::new();

    speaker(container.resolve()); // works fine!

    reader(container.resolve()); // not working as it wasn't for `dyn Read`
}



Complex Example

trait ComplexTrait<T: TRead> {
    fn do_something(&self);
}

This time, unlike the simpler example, this trait is generic trait whereby you need to take care of not just one trait but two. The following is its actual implementation;


struct ConcreteC<T> {
    dependency: T,
}

impl<T: TRead> ComplexTrait<T> for ConcreteC<T> {
    fn do_something(&self) {
        self.dependency.read()
    }
}

So far so good. let's register it;

// draw graph!
impl TRegister<dyn ComplexTrait<dyn TRead>, ConcreteC<ConcreteB>>
    for Container<dyn ComplexTrait<dyn TRead>>
{
    fn resolve(&self) -> ConcreteC<ConcreteB> {
        ConcreteC {
        	// note that dependency if from container. 
            dependency: Container::<dyn TRead>::new().resolve(), 
        }
    }
}

fn complex_reader<T: TRead>(r: impl ComplexTrait<T>) {
    r.do_something()
}
fn main() {
    let container = Container::<dyn TSpeak>::new();
    complex_reader(Container::<dyn ComplexTrait<dyn TRead>>::new().resolve())
}

Conclusion

To be honest, at first I wasn't sure if this would be possible to implement as Rust doens't have runtime reflection, only to find Rust type system and flexibility of trait gives much more freedom than I expected.

profile
Dude with existential crisis

1개의 댓글

comment-user-thumbnail
2024년 5월 28일

Trait mapping to its concrete implementation involves translating high-level characteristics or features into tangible, actionable plans and structures. This process is crucial in fields such as software development, genetics, and even construction. For instance, in construction, mapping the trait of durability into its concrete implementation might involve selecting specific materials and techniques that ensure longevity and resilience. A practical example of this is how contractors might use the services of https://www.elpasoconcretedrivewaycontractors.com to guarantee the durability of driveways. By understanding the desired traits—such as strength and low maintenance—and choosing appropriate concrete solutions, these contractors effectively implement these traits into a lasting and functional structure.

답글 달기

관련 채용 정보