Reference or owned for trait in parameter

It's best practice as far as I know to take reference even if owning the parameter is justified because references are often smaller than a struct. But for my example function I'm sure that 60% of the time, primitive types are used in place of my trait, and for primitive types it's best to not take references since their pointers are the same size or sometimes even larger than themselves. So is it possible to make this example function take owned or reference depending on the implementation of my trait?

fn add<T: MyTrait>(lhs: T, rhs: T) -> T {
    lhs + rhs
}

Edit: I found out I am missing something in the explanation. 'static is required by my trait

You can implement MyTrait for references. Say, if you were to implement MyTrait for u32 and for &u32, you could then decide at the callsite if you want to pass &u32 or u32 as T to add.

1 Like

It already does. A type parameter can stand in for any type. <T: MyTrait> means any type implementing MyTrait; it does not mean "any type implementing MyTrait except references".

If you are reading algebra, and you encounter a variable like n in N, you know it means any natural number. It is not "any natural number except those divisible by 3", even though you didn't write it as 3k.

1 Like

Thank you and very sorry that I left an important part of the explanation out. I need anything that implements my trait to be 'static because it's used in return types
If you need more context here is my actual trait
Note: I'm trying to make it only require Clone and not Copy and I've not been successful yet

That doesn't make any sense. Return types don't have to be 'static.

Furthermore, references that are not themselves a &'static _ reference do not satisfy 'static; that would lead to dangling references.

1 Like

They don’t in general, but notice @Jroid8’s original example:

fn add<T: MyTrait>(lhs: T, rhs: T) -> T {

You cannot (in most cases) implement this for T = &U because a new value of type T has to be constructed and returned. This is why the traits like std::ops::Add have a separate associated type for what they return. So, while a ’static bound per se isn’t required, this sort of signature still demands not using references. A different signature will be required to solve the original problem. (I'm not sure exactly what; I haven't previously thought about trying to make generic code that “does the right thing” with passing small values vs. references fully automatically.)

1 Like

This is my problem exactly. Is there not a way to write the function signature that does the right thing? so I don't have to write the whole function again

I don't know if it's the best idea, but you can

pub trait MyTrait:
    // Self + Self
    Add<Output = Self>
    // Self + AsArg<'_>
    + for<'any> Add<Self::AsArg<'any>, Output = Self>
    + Sized + 'static
{
    type AsArg<'a>: Borrow<Self>
        // AsArg<'_> + Self
        + Add<Self, Output = Self>
        // AsArg<'_> + AsArg<'_>
        + for<'b> Add<<Self as MyTrait>::AsArg<'b>, Output = Self>;
}

impl MyTrait for i32 {
    type AsArg<'a> = i32;
}

impl MyTrait for u128 {
    type AsArg<'a> = &'a u128;
}

When considering, be sure to note how in the playground calls to this version of the function are ambiguous without turbofish:

fn add_lr<T: MyTrait>(lhs: T::AsArg<'_>, rhs: T::AsArg<'_>) -> T {
    lhs + rhs
}

Because more than one implementor could write the same generic associated type definition, and the compiler doesn't try to go backwards from associated types to implementors.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.