Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the differences between a default Trait method and a parameterized function?

Tags:

rust

In Rust, I tend to have design issues when it comes to writing modules with traits. I'm not always sure whether I want:

pub trait Foo {
    fn foo(&self) -> bool;

    fn bar(&self) {
       if self.foo() {
           // Do something!
       } else {
           // Do something else!
       }
    }
}

Or

pub trait Foo {
    fn foo(&self) -> bool;
}

pub fn bar<T>(fooer: &T) where T: Foo {
    if fooer.foo() {
        // Do something!
    } else {
        // Do something else!
    }
}

(Of course, real examples are likely to have more elaborate traits or function signatures)

While issues of design are beyond the scope of Stack Overflow, I'm not entirely sure I even understand the meaningful, objective differences between the two, and I don't feel like browsing the standard library has shed much light. It seems like Rust's standard library prefers to use variable.method() as opposed to mod::function(&variable) in most cases. However, that still doesn't really answer the question since that's just a style guide argument rather than being based on objective knowledge about the difference.

Other than the obvious syntactic difference, what are the main functional differences between a default trait method and a module-level parameterized function? One big question I have is: does the default trait method monomorphize to use static dispatch, or does if it take self as if it were a trait object?

The only difference I'm seeing off the top of my head is that an impl of the trait may elect to override the default method implementation, hopefully/presumably providing an implementation that fulfills the same contract, while I'm guaranteed that the mod::function implementation always runs the exact same code no matter what (for better or worse). Is there anything else? Does the answer change if associated types or extension traits are involved?

like image 743
Linear Avatar asked Sep 24 '15 06:09

Linear


People also ask

What is the point of traits in Rust?

A trait tells the Rust compiler about functionality a particular type has and can share with other types. Traits are an abstract definition of shared behavior amongst different types. So, we can say that traits are to Rust what interfaces are to Java or abstract classes are to C++.

How do you implement a trait?

To implement a trait, declare an impl block for the type you want to implement the trait for. The syntax is impl <trait> for <type> . You'll need to implement all the methods that don't have default implementations. If your implementation is incomplete, your trusty friend the compiler will let you know.

Are Rust traits interfaces?

Rust is not an object oriented language. And traits are not exactly interfaces.

What is impl in Rust?

The impl keyword is primarily used to define implementations on types. Inherent implementations are standalone, while trait implementations are used to implement traits for types, or other traits. Functions and consts can both be defined in an implementation.


1 Answers

You actually answered the question yourself, congratulations!

Since Rust only has principled overloading/overriding via traits, the essential semantic difference is that a trait method can be overridden, and thus customized, while a free function cannot.

Technically, both Trait::func(&self) and mod::func<T: Trait>(&T) are monophormized while mod::func(&Trait) is not (and thus will incur the slight overhead of virtual calls).

Also, there is a slight memory overhead to Trait::func(&self): one more entry in the virtual table. It's probably unnoticeable.

In fine, the choice is generally a judgement call. Whether you open the door to customization or not is your choice.

like image 93
Matthieu M. Avatar answered Oct 10 '22 16:10

Matthieu M.