Contract establishment with type and trait implementation

How do I establish following contract in embedded rust project

**LibContract** crate
Pub struct BenCon;

Pub trait BenTrait {
 Fn1,
Fn2,
}

library crate Lib1 uses libCronract and should Provide BenCon structure type and implement BenTrait on it ?

Now this violates the orphan rule in rust. With C background, i am thinking in C. what would be correct idiomatic rust way of getting this done. What changes should I do? Here is my requirement, main application (binary crate) either can provide a reference to a BenCon object it created or can get BenCon object from lib1 and invoke methods on it. But the type and trait defintion can only reside in LibContract crate.

The natural thing to do in Rust would be to define the type BenCon in lib1.

Please explain why you believe it must be located in libContract. Telling us more about your requirements will help find a working solution.

(Perhaps you are thinking of libContract as being like the C header file for lib1? If so, then you should combine them into one library. Rust’s equivalent of header files (.rmeta) are automatically generated.)

Idea behind is to keep LibContract as the abstraction/layer between main application & HW. LibContract is the library given to HW vendors to implement & Lib1 is the library from vendor with the corresponding implementation.

If BenCon is left to vendors to provide in Lib1, then how to make sure all vendors provide BenCon ? How can this made in a contractual way than just in documentation saying vendor should provide this BenCon type?

If documentation is the only way, then I should too. But exploring if there is any there way?

Maybe something like a diamond pattern. On one point of the diamond, have only the trait; then all the vendors need to provide some implementation of that trait in the thick part of the diamond; and then a crate in the last point of the diamond depends on all the vendors, and uses feature flags or other conditional compilation to choose which vendor’s implementation to export as BenCon. Or, if you can’t compile all the HW vendors’ stuff at the same time, maybe you could look into what existing libraries like critical-section do: critical_section - Rust

The Rust way is not to say "this type must exist". The Rust way is to define a trait, which imposes obligations upon any type that implements it. The vendor's responsibility is not to provide a specific type name, but to provide (at least) one implementation of the trait (which might also imply implementations of other traits).

You can choose to go further and say "the convention shall be that the vendor crate exports a type named BenCon at the root", but that is a convention and not something that can be statically enforced (unless you write a conformance test suite that can be run against lib1).

How can this made in a contractual way than just in documentation saying vendor should provide this BenCon type?

Documentation is always necessary. If nothing else, there must be documentation specifying how the trait functions should behave, and that documentation must be respected by both caller and implementor.

If you want lib1 to have any machine-checked properties, you will need to set up and run tests against it. Libraries such as your libContract cannot enforce properties on their dependents.

1 Like

The Rust way is to define a trait, which imposes obligations upon any type that implements it

In that case, can you help to understand how application would get to know the type to create a instance/object?

let bencon_instance: ??

can you help with an example?

The application would be a dependent of lib1 and refer to lib1::BenCon.

let bencon_instance = lib1::BenCon::new();

It sounds like you may be imagining some specific scenario where this is not possible or not reasonable, perhaps because lib1 is provided by a different source than the application. If so, could you tell us more about that? How do you imagine libCommon, lib1, and the application being distributed? Who wrote each one? Who is responsible for putting them together into a working program?

working for a client at early stage of embedded rust adoption & feasibility study.

Strict HW abstraction is the most necessary goal. main application binary should be able to run if Lib1 from vendor1 is replaced with Lib2 from vendor2 (controlled by cargo cfg and only one vendor library would be configured to be built with and exposed (exported) under common name LibHal and binary application crate would import LibHal and LIbConract.

Will this abstraction oriented design paradigm work for embedded rust project?

only one vendor library would be configured to be built with and exposed (exported) under common name LibHal

This is an unusual configuration. Most Rust programs do not substitute different, separately developed code for the same package name. It is possible but not usual. Both packages would have to claim their actual name is LibHal instead of being Lib1 and Lib2.

The more usual thing would be for the application package to have conditional compilation set up to depend on either Lib1 or Lib2, or for LibHal to be a “facade” package that re-exports one of the underlying packages.

This is what I meant - wasn't clear enough

The more usual thing would be for the application package to have conditional compilation set up to depend on either Lib1 or Lib2 , or for LibHal to be a “facade” package that re-exports one of the underlying packages'