WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Rust · Guru · question 91 of 100

Discuss the evolution of Rust’s trait system and how it enables expressive, type-safe abstractions without runtime overhead.?

📕 Buy this interview preparation book: 100 Rust questions & answers — PDF + EPUB for $5

The trait system in Rust has gone through a few iterations over the years in response to developing community needs and use cases. In this answer, I will provide a brief overview of Rust’s trait system evolution and how it enables expressive, type-safe abstractions without runtime overhead.

## Trait Basics

Traits in Rust are similar to interfaces in other languages, but they have some unique features that make them especially powerful. Traits define a set of methods that types must implement to satisfy the trait. A type can implement multiple traits, and traits can have default method implementations.

    trait Animal {
        fn name(&self) -> &str;
        fn speak(&self);
    }
    
    struct Cat {
        name: String,
    }
    
    impl Animal for Cat {
        fn name(&self) -> &str {
            &self.name
        }
        
        fn speak(&self) {
            println!("{} says meow!", self.name);
        }
    }

In this example, we define an ‘Animal‘ trait with two methods: ‘name‘ and ‘speak‘. We then implement the ‘Animal‘ trait for a ‘Cat‘ struct. The ‘Cat‘ struct implements the ‘name‘ and ‘speak‘ methods, allowing it to be treated as an ‘Animal‘.

## First Iteration - RFC 447

The Rust team’s first attempt at a trait system was defined in [RFC 447](https://github.com/rust-lang/rfcs/blob/master/text/0447-final-traits.md) in 2012. This proposal introduced the basic notion of traits as we know them today but had some notable limitations. Most notably, traits could only be implemented for types defined in the same module as the trait. This restriction was limiting since it prevented traits from being used to define common functionality across modules.

The development of Rust continued rapidly, and as the language evolved, it became clear that the trait system needed to expand to meet the needs of the language.

## Second Iteration - RFC 1210

In 2015, Rust introduced a more powerful trait system through [RFC 1210](https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md). This new system allowed for "specialization," which means that traits could be further defined for specific types. This meant that a version of a trait could be defined once and then specifically refined as different types implemented it with more or less specific instructions.

    trait Animal {
        fn name(&self) -> &str;
        fn speak(&self);
        
        fn greeting(&self) -> String {
            format!("Hello, my name is {}", self.name())
        }
    }
    
    struct Cat {
        name: String,
    }
    
    impl Animal for Cat {
        fn name(&self) -> &str {
            &self.name
        }
        
        fn speak(&self) {
            println!("{} says meow!", self.name);
        }
        
        fn greeting(&self) -> String {
            format!("{} says {}", self.name(), self.speak())
        }
    }

In this example, we’ve added a ‘greeting‘ method to our ‘Animal‘ trait that provides a default implementation. The ‘Cat‘ struct has implemented this method and refined the default implementation to use its ‘name()‘ and ‘speak()‘ methods.

## Third Iteration - Today’s Trait System

The trait system in Rust has continued to evolve to become what it is today. One notable feature is that Rust’s trait system is statically dispatched, meaning that the behavior of a method is determined at compile time rather than runtime. This allows for the creation of type-safe abstractions that can be optimized by the compiler. For example, consider the following code:

    trait Animal {
        fn speak(&self);
    }
    
    fn speak_twice(a: &dyn Animal) {
        a.speak();
        a.speak();
    }
    
    struct Cat {}
    
    impl Animal for Cat {
        fn speak(&self) {
            println!("meow!");
        }
    }
    
    struct Dog {}
    
    impl Animal for Dog {
        fn speak(&self) {
            println!("woof!");
        }
    }
    
    fn main() {
        let cat = Cat {};
        speak_twice(&cat);
        
        let dog = Dog {};
        speak_twice(&dog);
    }

In this example, we define an ‘Animal‘ trait with a ‘speak‘ method and two structs, ‘Cat‘ and ‘Dog‘, that implement the ‘Animal‘ trait. We then define a function, ‘speak_twice‘ that accepts a ‘&dyn Animal‘. This allows us to pass in both a ‘Cat‘ and a ‘Dog‘ to the function, and the correct method implementation will be determined at compile time, rather than runtime.

Overall, Rust’s trait system has undergone a significant evolution since its early days. The current trait system enables expressive, type-safe abstractions without runtime overhead, allowing Rust code to be both safe and performant.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Rust interview — then scores it.
📞 Practice Rust — free 15 min
📕 Buy this interview preparation book: 100 Rust questions & answers — PDF + EPUB for $5

All 100 Rust questions · All topics