A trait is a set of function signatures that a type must implement, similar to an interface in Java or a pure-virtual base class in C++.
Why traits?
They let generic code state “any type that behaves like this”, so a function can work over many concrete types without runtime dispatch in the common case.
Defining and implementing
pub trait FinalGrade { fn final_grade(&self) -> f32;}impl FinalGrade for Enrolled_Student { fn final_grade(&self) -> f32 { /* average per syllabus */ }}
Notes on traits:
You can only define a trait on your own types, not on external ones, so you can’t silently break someone else’s code
A trait may supply a default implementation (something Java lacked for a long time)
A trait can appear as a function parameter or return type (generic-like)
+ combines multiple trait bounds on one parameter
Three traits ECE459 cares about:
Iterator, lets the compiler skip bounds checks while iterating, often faster than a manual for
Send, required to transfer ownership across threads. Almost all primitives have it, and types composed of Send types inherit it
Sync, &T can be shared across threads. Does not mean every operation on T is race-free, only that references can be handed out concurrently
Send without Sync
Some types (like Cell<T>) are Send but not Sync: ownership can move, but references can’t be shared. Implementing Sync wrong requires unsafe and has caused real bugs in the Rust ecosystem.