Complex trait requirements on struct - struct

I have a fairly complex trait set up and I'm having trouble lining the pieces up. Right now it looks roughly like this:
/// Trait for models which can be gradient-optimized.
pub trait Optimizable {
type Data;
type Target;
// The contract //
}
/// Trait for optimization algorithms.
pub trait OptimAlgorithm<M : Optimizable> {
// The contract //
}
Now I want to be able to allow a struct implementing OptimAlgorithm to be a field in a struct implementing Optimizable. This would look something like this:
/// Model struct
pub struct Model<A: OptimAlgorithm<Self>> {
alg: A,
}
impl Optimizable for Model<A> {
...
}
This doesn't work as the Self reference on the struct is nonsense. I tried using associated types for OptimAlgorithm but I need the algorithms to be generic over the models so this doesn't work. Is there a magic syntax I'm missing or does this need an overhaul?
Edit --
Here's a minimal example which shows error E0275 as described in Steven's answer. It's a little closer to my source code but less messy.

Just use Model<A> instead of Self. Self is only really useful in traits where one needs to be able to refer to the concrete type implementing the trait. Here, the concrete type is always Model<A>.
pub trait Optimizable {
type Data;
type Target;
// The contract //
}
/// Trait for optimization algorithms.
pub trait OptimAlgorithm<M: Optimizable> {
// The contract //
}
pub struct Model<A> where A: OptimAlgorithm<Model<A>> {
alg: A,
}
impl<A> Optimizable for Model<A>
where A: OptimAlgorithm<Model<A>>
{
type Data = ();
type Target = ();
}
In response to your updated code, the lifetime appears to be giving rust trouble. It appears you can make this work by using a higher-ranked lifetime but I don't know why.
pub trait Optimizable {
type Data;
type Target;
// The contract //
}
/// Trait for optimization algorithms.
pub trait OptimAlgorithm<M: Optimizable> {
// The contract //
}
pub struct Algorithm;
impl Default for Algorithm {
fn default() -> Algorithm { Algorithm }
}
impl<M: Optimizable> OptimAlgorithm<M> for Algorithm {
}
pub struct Model<'a, A> where for<'b> A: OptimAlgorithm<Model<'b, A>> {
layer_sizes: &'a [usize],
alg: A,
}
impl<'a, A> Model<'a, A>
where A: for<'b> OptimAlgorithm<Model<'b, A>>
{
pub fn new(layers: &'a [usize]) -> Model<Algorithm> {
Model {
layer_sizes: layers,
alg: Algorithm::default(),
}
}
}
impl<'a, A> Optimizable for Model<'a, A>
where A: for<'b> OptimAlgorithm<Model<'b, A>>
{
type Data = ();
type Target = ();
}
pub fn main() {
let layers = &[1usize,2,3];
let a = Model::<Algorithm>::new(layers as &[usize]);
}

I think it's a bug. Or at least surprising behaviour.
If you take off the where bound on the Model struct (and just leave it on the impl), your edited code compiles.
I'll try to reduce a bit more and file a bug.
pub trait Optimizable {
type Data;
type Target;
// The contract //
}
/// Trait for optimization algorithms.
pub trait OptimAlgorithm<M: Optimizable> {
// The contract //
}
pub struct Algorithm;
impl Default for Algorithm {
fn default() -> Algorithm { Algorithm }
}
impl<M: Optimizable> OptimAlgorithm<M> for Algorithm {
}
pub struct Model<'a, A> { // no bounds here
layer_sizes: &'a [usize],
alg: A,
}
impl<'a, A> Model<'a, A>
where A: OptimAlgorithm<Model<'a, A>>
{
pub fn new(layers: &[usize]) -> Model<Algorithm> {
Model {
layer_sizes: layers,
alg: Algorithm::default(),
}
}
}
impl<'a, A> Optimizable for Model<'a, A>
where A: OptimAlgorithm<Model<'a, A>>
{
type Data = ();
type Target = ();
}
pub fn main() {
}
playground

Related

Implement methods for trait without additional traits

Looking for "blanket" implementation of the method(s) for trait.
Let's say for a trait
pub trait A {
fn do_a(&self);
}
want to have boxed method that wraps with box, without introducing any additional traits:
fn boxed(self) -> Box<Self>;
I can have another trait to achieve that (playground)
pub trait A {
fn do_a(&self);
}
pub trait Boxed {
fn boxed(self) -> Box<Self>;
}
impl<T> Boxed for T
where
T: A,
{
fn boxed(self) -> Box<Self> {
Box::new(self)
}
}
However, new trait Boxed is required for that.
You can add boxed directly to A with a default implementation so that structs won't need to implement it themselves:
trait A {
fn do_a(&self);
fn boxed (self) -> Box<Self>
where Self: Sized
{
Box::new (self)
}
}
struct Foo{}
impl A for Foo {
fn do_a (&self) {
todo!();
}
// No need to redefine `boxed` here
}
fn main() {
let foo = Foo{};
let _object: Box<dyn A> = foo.boxed();
}
Playground

Composition and reusability in Rust compiled code

I am new to the composition approach with Rust, and I am having a hard time trying to figure out whether I could make my code more efficient / smaller.
Let us assume that I have a base struct BaseStruct:
struct BaseStruct {
values: Vec<i32>,
}
impl BaseStruct {
fn new() -> Self {
Self{values: vec![]}
}
}
Then, I define a AsBase trait to ease the composition process:
/ Trait used to do composition
trait AsBase {
fn as_base(&mut self) -> &mut BaseStruct;
// Note that add_val has a default implementation!
fn add_val(&mut self, val: i32) {
// let us assume that this method has hundreds of lines of code
self.as_base().values.push(val);
}
}
// Note that BaseStruct implements the AsBase trait!
impl AsBase for BaseStruct {
fn as_base(&mut self) -> &mut BaseStruct {
self
}
}
Note that BaseStruct implements the AsBase trait! Otherwise, we couldn't add a value to the vector.
Then, I derive the behavior of the base struct using composition:
// Derived struct and handy functions
struct DerivedStruct {
base: BaseStruct,
}
impl DerivedStruct {
fn new() -> Self {
Self{base: BaseStruct::new()}
}
}
// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
fn as_base(&mut self) -> &mut BaseStruct {
&mut self.base
}
}
So now, I can add values to the inner vector of my derived struct using the trait method!
fn main() {
let mut base = BaseStruct::new();
base.add_val(1);
let mut derived = DerivedStruct::new();
derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}
Here you have a playground with this example.
However, what if the add_val default method was very complex and required hundreds of lines of code? Would Rust generate a different method add_val for every struct implementing the AsBase trait? Or is the compiler smart enough to detect that they can share the same function?
Let me try to be clearer: would this alternative implementation be smaller is size, as it explicitly uses the same method?
// Base struct and handy associated methods
struct BaseStruct {
values: Vec<i32>,
}
impl BaseStruct {
fn new() -> Self {
Self{values: vec![]}
}
fn add_val(&mut self, val: i32) {
// Let us assume that this method is hundreds of lines long
self.values.push(val);
}
}
// Trait used to do composition
trait AsBase {
fn as_base(&mut self) -> &mut BaseStruct;
// Note that add_val has a default implementation!
fn add_val(&mut self, val: i32) {
self.as_base().add_val(val);
}
}
// Note that BaseStruct does NOT implement the AsBase trait to avoid naming collision!
// Derived struct and handy functions
struct DerivedStruct {
base: BaseStruct,
}
impl DerivedStruct {
fn new() -> Self {
Self{base: BaseStruct::new()}
}
}
// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
fn as_base(&mut self) -> &mut BaseStruct {
&mut self.base
}
}
fn main() {
let mut base = BaseStruct::new();
base.add_val(1);
let mut derived = DerivedStruct::new();
derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}
(Also, note that I couldn't implement the AsBase trait for BaseStruct due to naming collisions, I don't know if there is a workaround to avoid this other than changing the names of the methods).
Here you have a playground for the alternative version.
Yes, the compiler will generate a new instance of add_val() for each type. It may collapse them if they use the same machine code, but it will not if they don't. If you want to avoid that, a common way is to define a nested function (see Why does std::fs::write(...) use an inner function?):
fn add_val(&mut self, val: i32) {
fn inner(this: &mut BaseStruct) {
// let us assume that this method has hundreds of lines of code
base.values.push(val);
}
inner(self.as_base());
}
However, what you're doing is not composition. Rather, it's emulating inheritance with composition. When using the composition principle, you should not (usually) have an AsBase trait because you should not treat DerivedStruct as BaseStruct. This is not "is-a" relationship, this is a "has-a" relationship. If a method needs a BaseStruct, pass it a reference to the field directly and let it perform its work.

Trait as generic parameter to struct object intialization

I have this struct:
use sp_runtime::traits::Block;
struct Bar<T: Block> {
e1: Vec<T>,
}
impl<T: Block> Bar<T> {
pub fn new() -> Self {
Bar { e1: Vec::new() }
}
}
Where Block is from the sp_runtime crate.
In main:
fn main() {
let var_1 = Bar::<Block>::new();
}
Full Code
This code throws compilation error that trait can't be made into an object. I'm new to Rust, much of online solution haven't addressed this issue. Kindly let me know how to get around initialization of bar object.
Your confusion likely stems from the fact that the sp_runtime crate has two items called Block. One is the trait sp_runtime::traits::Block and the other is a struct, sp_runtime::generic::Block, which implements that trait.
Traits can be used as a constraint on a type parameter, but they cannot be used as a type argument.
So, in your definition of Bar<T>, you can constrain T with sp_runtime::traits::Block, but when you construct an instance of Bar<T>, T needs to be the struct instead.
use sp_runtime::traits::Block;
struct<T: Block> Bar {
e1: Vec<T>,
}
impl<T: Block> Bar<T> {
pub fn new() -> Self {
Bar {
e1: Vec::new(),
}
}
}
fn main() {
use sp_runtime::generic::Block;
let var_1 = Bar::<Block>::new();
}
However, given that this is the only implementation of the trait in the crate, you can just avoid mentioning the trait altogether and use the concrete struct type (unless you plan on implementing it yourself or depending on implementations from other crates):
use sp_runtime::generic::Block;
struct Bar{
e1 : Vec<Block>,
}
impl Bar {
pub fn new() -> Self{
Bar {
e1: Vec::new(),
}
}
}
fn main() {
let var_1 = Bar::new();
}

Why sized trait is required for a builder function to generate Rc<T>?

This code works fine (playground):
use std::rc::Rc;
trait Foo {
fn foo(&self);
}
struct Bar<T> {
v: Rc<T>,
}
impl<T> Bar<T> where
T: Foo {
fn new(rhs: Rc<T>) -> Bar<T> {
Bar{v: rhs}
}
}
struct Zzz {
}
impl Zzz {
fn new() -> Zzz {
Zzz{}
}
}
impl Foo for Zzz {
fn foo(&self) {
println!("Zzz foo");
}
}
fn make_foo() -> Rc<Foo> {
Rc::new(Zzz{})
}
fn main() {
let a = Bar::new(Rc::new(Zzz::new()));
a.v.as_ref().foo()
}
but if I make a wrapper to generate Rc like below, the compiler complains about missing sized trait (playground)
fn make_foo() -> Rc<dyn Foo> {
Rc::new(Zzz::new())
}
fn main() {
let a = Bar::new(make_foo());
a.v.as_ref().foo()
}
in both cases, Bar::new received parameters with same type Rc, why the rust compiler reacts different?
By default, all type variables are assumed to be Sized. For example, in the definition of the Bar struct, there is an implicit Sized constraint, like this:
struct Bar<T: Sized> {
v: Rc<T>,
}
The object dyn Foo cannot be Sized since each possible implementation of Foo could have a different size, so there isn't one size that can be chosen. But you are trying to instantiate a Bar<dyn Foo>.
The fix is to opt out of the Sized trait for T:
struct Bar<T: ?Sized> {
v: Rc<T>,
}
And also in the context of the implementations:
impl<T: ?Sized> Bar<T>
where
T: Foo
?Sized is actually not a constraint, but relaxing the existing Sized constraint, so that it is not required.
A consequence of opting out of Sized is that none of Bar's methods from that impl block can use T, except by reference.

Alias generic trait with default types

I have a trait that is generic: trait Trait<T> and I want to create another trait that specifies the generics: type Alias = Trait<String>. This would allow impl Alias for T and not have to specify the type parameters. I tried a couple ways of doing this and haven't found any that works.
This is not a duplicate of Type alias for multiple traits or Aliasing trait with associated types because doing trait Alias: Trait<T> requires people to implement Trait<T> anyway. I want to offer a trait that hides the generics.
A clearer code sample:
trait DefaultEvents = Events<UserStruct, ChannelStruct, IrcStruct>;
struct MyHandler;
impl DefaultEvents for MyHandler {
...
}
Here's my best suggestion, it's going to mean a bit more work on your part (with lots of manual trait inheritance), but it should achieve the user convenience that you want.
pub mod user_friendly {
pub trait GivesNum<T> {
fn get_num(&self) -> T;
}
pub trait GivesDouble {
fn get_double(&self) -> f64;
}
impl<S> GivesNum<f64> for S where S: GivesDouble {
fn get_num(&self) -> f64 { self.get_double() }
}
}
// now your library's user needs to do less
use user_friendly::*;
struct MyStruct { num: f64 }
impl GivesDouble for MyStruct {
fn get_double(&self) -> f64 { 2.0 * self.num }
}
fn main() {
let s = MyStruct{ num: 5.0 };
println!("MyStruct.get_num() = {}", s.get_num());
}
Try it on Rust Playground

Resources