{
fn magnitude(&self) -> f32 {
((self.x * self.x) + (self.y * self.y)).sqrt()
}
}
fn main() {
let v1 = Vector::new(1.0, 1.0);
println!("{}", v1.magnitude());
let v2 = Vector::new(1, 1);
// println!("{}", v2.magnitude());
}
```
Note:
Can I call `my_vector.magnitude()` if T is ... a String? A Person? A TCPStream?
Are there some trait bounds we could place on `T` such that `T + T -> T` and `T * T -> T` and `T::sqrt()` were all available?
---
## The error:
error[E0599]: no method named `magnitude` found for struct `Vector<{integer}>` in the current scope
--> src/main.rs:23:23
|
2 | struct Vector<T> {
| ---------------- method `magnitude` not found for this struct
...
23 | println!("{}", v2.magnitude());
| ^^^^^^^^^ method not found in `Vector<{integer}>`
|
= note: the method was found for
- `Vector<f32>`
For more information about this error, try `rustc --explain E0599`.
---
## Adding Bounds
* Generics aren't much use without bounds.
* A bound says which traits must be implemented on any type used for that type parameter
* You can apply the bounds on the type, or a function/method, or both.
---
## Adding Bounds - Example
```rust []
trait HasArea {
fn area(&self) -> f32;
}
fn print_area(shape: &T) where T: HasArea {
let area = shape.area();
println!("Area = {area:?}");
}
struct UnitSquare;
impl HasArea for UnitSquare {
fn area(&self) -> f32 {
1.0
}
}
fn main() {
let u = UnitSquare;
print_area(&u);
}
```
---
## Adding Bounds - Alt. Example
```rust []
trait HasArea {
fn area(&self) -> f32;
}
fn print_area(shape: &T) {
let area = shape.area();
println!("Area = {area:?}");
}
struct UnitSquare;
impl HasArea for UnitSquare {
fn area(&self) -> f32 {
1.0
}
}
fn main() {
let u = UnitSquare;
print_area(&u);
}
```
Note:
This is exactly equivalent to the previous example, but shorter. However, if you
end up with a large set of bounds, they are easier to format when at the end of
the line.
---
## General Rule
* If you can, try and avoid adding bounds to `structs`.
* Simpler to only add them to the methods.
---
## Multiple Bounds
You can specify multiple bounds.
```rust []
trait HasArea {
fn area(&self) -> f32;
}
fn print_area(shape: &T) {
println!("Shape {:?} has area {}", shape, shape.area());
}
#[derive(Debug)]
struct UnitSquare;
impl HasArea for UnitSquare {
fn area(&self) -> f32 { 1.0 }
}
fn main() {
let u = UnitSquare;
print_area(&u);
}
```
---
## impl Trait
* The `impl Trait` syntax in argument position was just *syntactic sugar*.
* (It does something special in the return position though)
```rust []
trait HasArea {
fn area_m2(&self) -> f64;
}
struct AreaCalculator {
area_m2: f64
}
impl AreaCalculator {
// Same: fn add(&mut self, shape: impl HasArea) {
fn add(&mut self, shape: T) {
self.area_m2 += shape.area_m2();
}
}
```
Note:
Some types that cannot be written out, like the closure, can be expressed as return types using `impl`. e.g. `fn score(y: i32) -> impl Fn(i32) -> i32`.
---
## Caution
* Using Generics is *Hard Mode Rust*
* Don't reach for it in the first instance...
* Try and just use concrete types?
---
## Generic over Constants
In Rust 1.51, we gained the ability to be generic over *constant values* too.
```rust []
struct Polygon {
colour: u32
}
impl Polygon {
fn new(colour: u32) -> Polygon { Polygon { colour } }
fn print(&self) { println!("{} sides, colour=0x{:06x}", SIDES, self.colour); }
}
fn main() {
let triangle: Polygon<3> = Polygon::new(0x00FF00);
triangle.print();
}
```
Note:
`SIDES` is a property of the type, and doesn't occupy any memory within any
values of that type at run-time - the constant is pasted in wherever it is used.
---
## Generic Traits
Traits themselves can have type parameters too!
```rust []
trait HasArea {
fn area(&self) -> T;
}
// Here we only accept a shape where the `U` in `HasArea` is printable
fn print_area(shape: &T) where T: HasArea, U: std::fmt::Debug {
let area = shape.area();
println!("Area = {area:?}");
}
struct UnitSquare;
impl HasArea for UnitSquare {
fn area(&self) -> f64 {
1.0
}
}
fn main() {
let u = UnitSquare;
print_area(&u);
}
```
---
## Special Bounds
* Some bounds apply automatically
* Special syntax to *turn them off*
```rust []
fn print_debug(value: &T) {
println!("value is {:?}", value);
}
```
Note:
This bound says "It must implement std::fmt::Debug, but I don't care if it has a size known at compile-time".
Things that don't have sizes known at compile time (but which may or may not implement std::fmt::Debug) include:
* String Slices
* Closures