1466 lines
42 KiB
Text
1466 lines
42 KiB
Text
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Traits"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Defining Traits"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* Traits are Rust’s take on interfaces or abstract base classes\n",
|
||
"* They define shared behavior that types can implement\n",
|
||
"* They enable polymorphism and code reuse"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"trait Shape {\n",
|
||
" fn move_by(&mut self, dx: i32, dy: i32);\n",
|
||
" fn area(&self) -> f64;\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Implementing Traits\n",
|
||
"\n",
|
||
"* To implement a trait for a type, we use the `impl` keyword"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"#[derive(Debug, Copy, Clone)]\n",
|
||
"struct Point {\n",
|
||
" x: i32,\n",
|
||
" y: i32\n",
|
||
"}\n",
|
||
"\n",
|
||
"#[derive(Debug, Copy, Clone)]\n",
|
||
"struct Circle {\n",
|
||
" position: Point,\n",
|
||
" radius: u32\n",
|
||
"}\n",
|
||
"\n",
|
||
"#[derive(Debug, Copy, Clone)]\n",
|
||
"struct Rectangle {\n",
|
||
" position: Point,\n",
|
||
" width: u32,\n",
|
||
" height: u32\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl Shape for Circle {\n",
|
||
" fn move_by(&mut self, dx: i32, dy: i32) {\n",
|
||
" self.position.x += dx;\n",
|
||
" self.position.y += dy;\n",
|
||
" }\n",
|
||
"\n",
|
||
" fn area(&self) -> f64 {\n",
|
||
" std::f64::consts::PI * (self.radius as f64) * (self.radius as f64)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl Shape for Rectangle {\n",
|
||
"\n",
|
||
" fn move_by(&mut self, dx: i32, dy: i32) {\n",
|
||
" self.position.x += dx;\n",
|
||
" self.position.y += dy;\n",
|
||
" }\n",
|
||
"\n",
|
||
" fn area(&self) -> f64 {\n",
|
||
" (self.width * self.height) as f64\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* We can implement many traits for a type:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Drawing the circle at Point { x: 0, y: 0 } with radius: 10\n",
|
||
"Drawing the rectangle at Point { x: 0, y: 0 } with width: 10 and height: 20\n",
|
||
"--- after move:\n",
|
||
"Drawing the circle at Point { x: 10, y: 20 } with radius: 10\n",
|
||
"Drawing the rectangle at Point { x: 10, y: 20 } with width: 10 and height: 20\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"trait Drawable {\n",
|
||
" fn draw(&self);\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl Drawable for Circle {\n",
|
||
" fn draw(&self) {\n",
|
||
" println!(\"Drawing the circle at {:?} with radius: {}\", self.position, self.radius);\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl Drawable for Rectangle {\n",
|
||
" fn draw(&self) {\n",
|
||
" println!(\"Drawing the rectangle at {:?} with width: {} and height: {}\", self.position, self.width, self.height);\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let mut c = Circle { position: Point { x: 0, y: 0 }, radius: 10 };\n",
|
||
" let mut r = Rectangle { position: Point { x: 0, y: 0 }, width: 10, height: 20 };\n",
|
||
"\n",
|
||
" c.draw();\n",
|
||
" r.draw();\n",
|
||
"\n",
|
||
" c.move_by(10, 20);\n",
|
||
" r.move_by(10, 20);\n",
|
||
"\n",
|
||
" println!(\"--- after move:\");\n",
|
||
"\n",
|
||
" c.draw();\n",
|
||
" r.draw();\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Trait Bounds\n",
|
||
"\n",
|
||
"* Trait bounds specify that a generic can be any type that implements a trait. \n",
|
||
"* They work like a contracts at compile time\n",
|
||
"\n",
|
||
"### impl Trait Syntax\n",
|
||
"\n",
|
||
"* The `impl Trait` syntax is a shorthand for a longer form, called a **trait bound**"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fn draw_shape(shape: &impl Drawable) {\n",
|
||
" shape.draw()\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Drawing the circle at Point { x: 10, y: 20 } with radius: 5\n",
|
||
"Drawing the rectangle at Point { x: 40, y: 50 } with width: 10 and height: 5\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"let mut circle = Circle{position: Point{x: 10, y: 20}, radius: 5};\n",
|
||
"let mut rect = Rectangle{position: Point{x: 40, y: 50}, width: 10, height: 5};\n",
|
||
"\n",
|
||
"draw_shape(&circle);\n",
|
||
"draw_shape(&rect);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Trait Bound Syntax"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Drawing the circle at Point { x: 10, y: 20 } with radius: 5\n",
|
||
"Drawing the rectangle at Point { x: 40, y: 50 } with width: 10 and height: 5\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"fn draw_shape<T: Drawable>(shape: &T) {\n",
|
||
" shape.draw()\n",
|
||
"}\n",
|
||
"\n",
|
||
"draw_shape(&circle);\n",
|
||
"draw_shape(&rect);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Many trait bounds"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* We can specify multiple trait bounds on a generic type by using the `+` syntax"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fn use_shape(shape: &mut (impl Shape + Drawable)) {\n",
|
||
" shape.move_by(10, 15);\n",
|
||
" shape.draw();\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fn use_shape<T: Shape + Drawable>(shape: &mut T) {\n",
|
||
" shape.move_by(10, 15);\n",
|
||
" shape.draw();\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* We can use a `where` clause to specify trait bounds on generic types"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fn use_shape<T>(shape: &mut T)\n",
|
||
"where \n",
|
||
" T: Shape + Drawable \n",
|
||
"{\n",
|
||
" shape.move_by(10, 15);\n",
|
||
" shape.draw();\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use_shape(&mut circle);\n",
|
||
"use_shape(&mut rect);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Returning Types that Implement Traits"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Drawing the circle at Point { x: 0, y: 0 } with radius: 1\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"fn create_drawable(id: &str) -> Box<dyn Drawable> {\n",
|
||
" match id {\n",
|
||
" \"Circle\" => Box::new(Circle{position: Point{x: 0, y: 0}, radius: 1}),\n",
|
||
" \"Rectangle\" => Box::new(Rectangle{position: Point{x: 0, y: 0}, width: 2, height: 1}),\n",
|
||
" _ => panic!(\"Unknown shape\")\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"let shape: Box<dyn Drawable> = create_drawable(\"Circle\");\n",
|
||
"shape.draw();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Self in Traits\n",
|
||
"\n",
|
||
"* `Self` is an alias for the type we’re implementing the trait for"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"trait Cloneable {\n",
|
||
" fn clone(&self) -> Self; // the type of x.clone() is the same as the type of x, whatever that might be\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* A trait that uses the `Self` type is incompatible with trait objects\n",
|
||
"* Trait objects require dynamic dispatch, but using `Self` in a trait implies static dispatch — the compiler must know the concrete type at compile time.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "the trait `Cloneable` is not dyn compatible",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0038] Error:\u001b[0m the trait `Cloneable` is not dyn compatible",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_12:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249mf\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54my\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54mC\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mb\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mB\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m<\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mC\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `Cloneable` is not dyn compatible",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"fn do_clone(prototype: &dyn Cloneable) -> Box<dyn Cloneable> {\n",
|
||
" let cloned_object = prototype.clone();\n",
|
||
" Box::new(cloned_object)\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Trait Objects\n",
|
||
"\n",
|
||
"* A trait object allows to implement dynamic polymorphism in Rust"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "mismatched types",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0308] Error:\u001b[0m mismatched types",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_13:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100my\u001b[0m\u001b[38;5;100mn\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100mS\u001b[0m\u001b[38;5;100mh\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mc\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54mc\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mz\u001b[0m\u001b[38;5;249me\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m expected due to this",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m expected `dyn Shape`, found `Circle`",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
},
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "the size for values of type `dyn Shape` cannot be known at compilation time",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0277] Error:\u001b[0m the size for values of type `dyn Shape` cannot be known at compilation time",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_13:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mh\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mz\u001b[0m\u001b[38;5;249me\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m \u001b[38;5;100m│\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m doesn't have a size known at compile-time",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider borrowing here: `&`",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"let circle = Circle{position: Point{x: 10, y: 20}, radius: 5};\n",
|
||
"\n",
|
||
"let shape: dyn Shape = circle; // ERROR: Shape does not have a constant size"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### References to Trait"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* A reference to trait type, like `Shape` is called **trait object**. Like any other reference, a trait object points to some value, it has a lifetime, and it can be either mut or shared.\n",
|
||
"* In memory, a trait object is a **fat pointer** consisting of a pointer to the value, plus a pointer to a table representing that value’s type (virtual table).\n",
|
||
"\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Shape area: 78.54\n",
|
||
"Shape area: 200.00\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"fn main() {\n",
|
||
" let mut shape: &dyn Shape;\n",
|
||
" \n",
|
||
" let circle = Circle{position: Point{x: 10, y: 20}, radius: 5};\n",
|
||
" shape = &circle;\n",
|
||
" println!(\"Shape area: {:.2}\", shape.area());\n",
|
||
"\n",
|
||
" shape = &Rectangle{position: Point{x: 10, y: 20}, width: 10, height: 20};\n",
|
||
" println!(\"Shape area: {:.2}\", shape.area());\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Boxes to Trait\n",
|
||
"\n",
|
||
"* We can also use a `Box` to store a trait object\n",
|
||
"* A `Box<dyn Trait>` is a trait object that points to a value on the heap"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Shape area: 78.54\n",
|
||
"Shape area: 200.00\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"let box_circle: Box<Circle> = Box::new(Circle{position: Point{x: 10, y: 20}, radius: 5});\n",
|
||
"let mut shape: Box<dyn Shape> = box_circle; // OK - Box<dyn Shape> is trait object\n",
|
||
"println!(\"Shape area: {:.2}\", shape.area());\n",
|
||
"\n",
|
||
"shape = Box::new(Rectangle{position: Point{x: 10, y: 20}, width: 10, height: 20});\n",
|
||
"println!(\"Shape area: {:.2}\", shape.area());"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Dynamic Polymorphism in Rust"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Shape area: 78.54\n",
|
||
"Shape area: 50.00\n",
|
||
"Shape area: 49.00\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"#[derive(Debug)]\n",
|
||
"struct Square {\n",
|
||
" position: Point,\n",
|
||
" side: u32\n",
|
||
"} \n",
|
||
"\n",
|
||
"impl Shape for Square {\n",
|
||
"\n",
|
||
" fn move_by(&mut self, dx: i32, dy: i32) {\n",
|
||
" self.position.x += dx;\n",
|
||
" self.position.y += dy;\n",
|
||
" }\n",
|
||
"\n",
|
||
" fn area(&self) -> f64 {\n",
|
||
" (self.side * self.side) as f64\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl Drawable for Square {\n",
|
||
" fn draw(&self) {\n",
|
||
" println!(\"Drawing the square at {:?} with side: {}\", self.position, self.side);\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let mut shapes: Vec<Box<dyn Shape>> = vec![\n",
|
||
" Box::new(Circle{position: Point{x: 10, y: 20}, radius: 5}),\n",
|
||
" Box::new(Rectangle{position: Point{x: 40, y: 50}, width: 10, height: 5}),\n",
|
||
" Box::new(Square{position: Point{x: 70, y: 80}, side: 7})\n",
|
||
" ];\n",
|
||
"\n",
|
||
" for shape in shapes.iter_mut() {\n",
|
||
" shape.move_by(5, 5);\n",
|
||
" println!(\"Shape area: {:.2}\", shape.area());\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Combining Traits\n",
|
||
"\n",
|
||
"* Trait objects can only use one trait at a time, but you can work around this by creating a supertrait:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Drawing the circle at Point { x: 20, y: 35 } with radius: 5\n",
|
||
"Drawing the rectangle at Point { x: 50, y: 65 } with width: 10 and height: 5\n",
|
||
"Drawing the square at Point { x: 80, y: 95 } with side: 7\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"trait DrawableShape: Shape + Drawable {} // Trait that combines Shape and Drawable - a supertrait\n",
|
||
"\n",
|
||
"impl<T> DrawableShape for T \n",
|
||
" where T: Shape + Drawable \n",
|
||
"{} // Implement DrawableShape for any type that implements Shape and Drawable - blanket implementation\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let mut drawable_shapes: Vec<Box<dyn DrawableShape>> = vec![\n",
|
||
" Box::new(Circle{position: Point{x: 10, y: 20}, radius: 5}),\n",
|
||
" Box::new(Rectangle{position: Point{x: 40, y: 50}, width: 10, height: 5}),\n",
|
||
" Box::new(Square{position: Point{x: 70, y: 80}, side: 7})\n",
|
||
" ];\n",
|
||
"\n",
|
||
" for shape in drawable_shapes.iter_mut() {\n",
|
||
" shape.move_by(10, 15);\n",
|
||
" shape.draw();\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Orphan Rules\n",
|
||
"\n",
|
||
"* The **orphan rule** prevent us from implementing a foreign trait on a foreign type\n",
|
||
"* **You are allowed to implement any trait on any type, as long as either the trait or the type is local to your crate**"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Extension traits"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"// Extension trait for checking if a character is an emoji\n",
|
||
"trait IsEmoji {\n",
|
||
" fn is_emoji(&self) -> bool;\n",
|
||
"}\n",
|
||
"\n",
|
||
"// Implementation of the IsEmoji trait for the char type\n",
|
||
"impl IsEmoji for char {\n",
|
||
" fn is_emoji(&self) -> bool {\n",
|
||
" let c = *self as u32;\n",
|
||
" (c >= 0x1F600 && c <= 0x1F64F) || (c >= 0x1F300 && c <= 0x1F5FF) || (c >= 0x1F680 && c <= 0x1F6FF) || (c >= 0x1F900 && c <= 0x1F9FF)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let c = '😀';\n",
|
||
" println!(\"Is '{}' an emoji? {}\", c, c.is_emoji());\n",
|
||
" println!(\"Is '{}' an emoji? {}\", '@', '@'.is_emoji());\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* `IsEmoji` is an *extension trait* - adds a method to an existing type"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Generic `impl` blocks\n",
|
||
"\n",
|
||
"* A generic `impl` block for a trait allows you to implement a trait for any type that satisfies certain bounds. This is a powerful way to write reusable, composable behavior across many types."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::fmt::Debug;\n",
|
||
"\n",
|
||
"trait DrawInDebug {\n",
|
||
" fn draw_dbg(&self);\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl<T: Debug + Drawable> DrawInDebug for T {\n",
|
||
" fn draw_dbg(&self) {\n",
|
||
" println!(\"Drawing a {:?}\", self);\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"circle.draw_dbg();\n",
|
||
"rect.draw_dbg();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Associated Types\n",
|
||
"\n",
|
||
"* Associated types are a way to associate a type placeholder with a trait\n",
|
||
"* Each type that implements the trait can specify its own concrete type for the associated type"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 20,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"0\n",
|
||
"1\n",
|
||
"2\n",
|
||
"3\n",
|
||
"4\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"trait Iterator {\n",
|
||
" type Item; // associated type\n",
|
||
" fn next(&mut self) -> Option<Self::Item>;\n",
|
||
"}\n",
|
||
"\n",
|
||
"struct Counter {\n",
|
||
" value: u32,\n",
|
||
" sentinel: u32\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl Counter {\n",
|
||
" fn new(sentinel: u32) -> Counter {\n",
|
||
" Counter{value: 0, sentinel}\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl Iterator for Counter {\n",
|
||
" type Item = u32; // definition of associated type for Counter\n",
|
||
"\n",
|
||
" fn next(&mut self) -> Option<Self::Item> {\n",
|
||
" if self.value < self.sentinel {\n",
|
||
" let result = self.value;\n",
|
||
" self.value += 1;\n",
|
||
" Some(result)\n",
|
||
" } else {\n",
|
||
" None\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let mut counter = Counter::new(5);\n",
|
||
"\n",
|
||
" while let Some(value) = counter.next() {\n",
|
||
" println!(\"{}\", value);\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* Generic code can use associated types:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fn collect_into_vector<I: Iterator>(mut iter: I) -> Vec<I::Item> // I::Item is the associated type of the Iterator\n",
|
||
"{\n",
|
||
" let mut results = Vec::new();\n",
|
||
"\n",
|
||
" while let Some(value) = iter.next() {\n",
|
||
" results.push(value);\n",
|
||
" }\n",
|
||
"\n",
|
||
" results\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() { \n",
|
||
" let numbers = collect_into_vector(Counter::new(5));\n",
|
||
" println!(\"Collected numbers: {:?}\", numbers);\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Subtraits\n",
|
||
"\n",
|
||
"* A trait can be an extension of another trait"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 21,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Forward iteration:\n",
|
||
"Counter value: 0\n",
|
||
"Counter value: 1\n",
|
||
"Counter value: 2\n",
|
||
"Counter value: 3\n",
|
||
"Counter value: 4\n",
|
||
"\n",
|
||
"Backward iteration:\n",
|
||
"Counter value: 4\n",
|
||
"Counter value: 3\n",
|
||
"Counter value: 2\n",
|
||
"Counter value: 1\n",
|
||
"Counter value: 0\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"trait BidirectionalIterator: Iterator {\n",
|
||
" fn prev(&mut self) -> Option<Self::Item>;\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl BidirectionalIterator for Counter {\n",
|
||
" fn prev(&mut self) -> Option<Self::Item> {\n",
|
||
" if self.value > 0 {\n",
|
||
" let result = self.value;\n",
|
||
" self.value -= 1;\n",
|
||
" Some(self.value)\n",
|
||
" } else {\n",
|
||
" None\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let mut counter = Counter::new(5);\n",
|
||
"\n",
|
||
" println!(\"Forward iteration:\");\n",
|
||
" while let Some(value) = counter.next() {\n",
|
||
" println!(\"Counter value: {}\", value);\n",
|
||
" }\n",
|
||
"\n",
|
||
" println!(\"\\nBackward iteration:\");\n",
|
||
" while let Some(value) = counter.prev() {\n",
|
||
" println!(\"Counter value: {}\", value);\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Static Methods in Traits\n",
|
||
"\n",
|
||
"* Traits can have static methods"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 22,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Creating set:\n",
|
||
"Set contains 'apple': true\n",
|
||
"Set contains 'grape': false\n",
|
||
"\n",
|
||
"Creating set from slice:\n",
|
||
"Set contains 'apple': true\n",
|
||
"Set contains 'grape': false\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"trait StringSet {\n",
|
||
" /// Creates a new, empty set. - static method\n",
|
||
" fn new() -> Self;\n",
|
||
"\n",
|
||
" /// Creates a set from a slice of strings. - static method\n",
|
||
" fn from_slice(values: &[&str]) -> Self;\n",
|
||
"\n",
|
||
" /// Adds a string to the set.\n",
|
||
" fn add(&mut self, value: &str);\n",
|
||
"\n",
|
||
" /// Removes a string from the set.\n",
|
||
" fn contains(&self, value: &str) -> bool;\n",
|
||
"}\n",
|
||
"\n",
|
||
"struct HashSet {\n",
|
||
" data: std::collections::HashSet<String>\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl StringSet for HashSet {\n",
|
||
" fn new() -> Self {\n",
|
||
" HashSet{data: std::collections::HashSet::new()}\n",
|
||
" }\n",
|
||
"\n",
|
||
" fn from_slice(values: &[&str]) -> Self {\n",
|
||
" let mut set = HashSet::new();\n",
|
||
" for value in values {\n",
|
||
" set.add(value);\n",
|
||
" }\n",
|
||
" set\n",
|
||
" }\n",
|
||
"\n",
|
||
" fn add(&mut self, value: &str) {\n",
|
||
" self.data.insert(value.to_string());\n",
|
||
" }\n",
|
||
"\n",
|
||
" fn contains(&self, value: &str) -> bool {\n",
|
||
" self.data.contains(value)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" println!(\"Creating set:\");\n",
|
||
" let mut set = HashSet::new();\n",
|
||
" set.add(\"apple\");\n",
|
||
" set.add(\"banana\");\n",
|
||
" set.add(\"orange\");\n",
|
||
"\n",
|
||
" println!(\"Set contains 'apple': {}\", set.contains(\"apple\"));\n",
|
||
" println!(\"Set contains 'grape': {}\", set.contains(\"grape\"));\n",
|
||
"\n",
|
||
" println!(\"\\nCreating set from slice:\");\n",
|
||
"\n",
|
||
" let set = HashSet::from_slice(&[\"apple\", \"banana\", \"orange\"]);\n",
|
||
" println!(\"Set contains 'apple': {}\", set.contains(\"apple\"));\n",
|
||
" println!(\"Set contains 'grape': {}\", set.contains(\"grape\"));\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Fully qualified methods\n",
|
||
"\n",
|
||
"* A fully qualified method call is a way to explicitly specify which trait’s method you want to call, especially when:\n",
|
||
" - Multiple traits define methods with the same name.\n",
|
||
" - You’re calling a trait method that isn’t in scope via automatic dispatch.\n",
|
||
" - You want to call a method on a trait for a type that implements it, but the method isn’t available via dot syntax.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Drawing a deck of cards on the screen\n",
|
||
"Drawing a card from the deck\n",
|
||
"Drawing a card from the deck\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"mod FullyQualifiedSyntax {\n",
|
||
" \n",
|
||
" pub trait Drawable {\n",
|
||
" fn draw(&self);\n",
|
||
" }\n",
|
||
"\n",
|
||
" pub trait Deck {\n",
|
||
" fn draw(&self) -> String;\n",
|
||
" }\n",
|
||
"\n",
|
||
" pub struct DeckOfCards;\n",
|
||
"\n",
|
||
" impl Drawable for DeckOfCards {\n",
|
||
" fn draw(&self) {\n",
|
||
" println!(\"Drawing a deck of cards on the screen\");\n",
|
||
" }\n",
|
||
" }\n",
|
||
"\n",
|
||
" impl Deck for DeckOfCards {\n",
|
||
" fn draw(&self) -> String {\n",
|
||
" println!(\"Drawing a card from the deck\");\n",
|
||
" \"Ace of Spades\".to_string()\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"use FullyQualifiedSyntax::*;\n",
|
||
"\n",
|
||
"let deck_of_cards = DeckOfCards;\n",
|
||
"\n",
|
||
"// deck_of_cards.draw(); // ambigous call of draw() method\n",
|
||
"\n",
|
||
"FullyQualifiedSyntax::Drawable::draw(&deck_of_cards);\n",
|
||
"Deck::draw(&deck_of_cards);\n",
|
||
"<DeckOfCards as Deck>::draw(&deck_of_cards);"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Reverse-Engineering Bounds\n",
|
||
"\n",
|
||
"* When writing generic function or structs with trait bounds, it is often useful to reverse-engineer the bounds from the function body.\n",
|
||
"* This approach helps in understanding the minimum requirements for the types used in generics.\n",
|
||
"* Compiler errors can guide the addition of necessary trait bounds."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### First attempt"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fn dot<N>(v1: &[N], v2: &[N]) -> N {\n",
|
||
" let mut total: N = N::default();\n",
|
||
" for i in 0 .. v1.len() {\n",
|
||
" total = total + v1[i] * v2[i];\n",
|
||
" }\n",
|
||
" total\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### After reading compiler errors"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::ops::{Add, Mul};\n",
|
||
"\n",
|
||
"fn dot<N: Default + Mul<Output=N> + Add<Output=N> + Copy>(v1: &[N], v2: &[N]) -> N \n",
|
||
"{\n",
|
||
" let mut total: N = N::default();\n",
|
||
" for i in 0 .. v1.len() {\n",
|
||
" total = total + v1[i] * v2[i];\n",
|
||
" }\n",
|
||
" total\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"let v1 = [1, 2, 3];\n",
|
||
"let v2 = vec![4, 5, 6];\n",
|
||
"\n",
|
||
"dot(&v1[..], &v2[..])"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Generics"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Generic Function"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fn min<T: Ord>(a: T, b: T) -> T {\n",
|
||
" if a < b {\n",
|
||
" a\n",
|
||
" } else {\n",
|
||
" b\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 25,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"1"
|
||
]
|
||
},
|
||
"execution_count": 25,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"min(1, 2)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"\"hello\""
|
||
]
|
||
},
|
||
"execution_count": 26,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"min(\"hello\", \"world\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Generic Structure\n",
|
||
"\n",
|
||
"* Generic structures allow us to define data types that can hold values of any type"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Popped: 3\n",
|
||
"Popped: 2\n",
|
||
"Popped: 1\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"struct Stack<T> {\n",
|
||
" items: Vec<T>\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl<T> Stack<T> {\n",
|
||
" fn push(&mut self, item: T) {\n",
|
||
" self.items.push(item);\n",
|
||
" }\n",
|
||
"\n",
|
||
" fn pop(&mut self) -> Option<T> {\n",
|
||
" self.items.pop()\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let mut stack = Stack{items: Vec::new()};\n",
|
||
" stack.push(1);\n",
|
||
" stack.push(2);\n",
|
||
" stack.push(3);\n",
|
||
"\n",
|
||
" while let Some(item) = stack.pop() {\n",
|
||
" println!(\"Popped: {}\", item);\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Generic Traits\n",
|
||
"\n",
|
||
"* Traits can also be generic over types"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 28,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" /// std::ops::Mul, the trait for types that support `*`.\n",
|
||
" pub trait Mul<RHS=Self> {\n",
|
||
" /// The resulting type after applying the `*` operator\n",
|
||
" type Output;\n",
|
||
"\n",
|
||
" /// The method for the `*` operator\n",
|
||
" fn mul(self, rhs: RHS) -> Self::Output;\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 29,
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Result: -5 + 10i\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"#[derive(Debug)]\n",
|
||
"struct Complex {\n",
|
||
" real: f64,\n",
|
||
" imag: f64\n",
|
||
"}\n",
|
||
"\n",
|
||
"use std::ops::Mul;\n",
|
||
"\n",
|
||
"impl Mul for Complex {\n",
|
||
" type Output = Complex;\n",
|
||
"\n",
|
||
" fn mul(self, rhs: Complex) -> Complex {\n",
|
||
" Complex {\n",
|
||
" real: self.real * rhs.real - self.imag * rhs.imag,\n",
|
||
" imag: self.real * rhs.imag + self.imag * rhs.real\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let a = Complex{real: 1.0, imag: 2.0};\n",
|
||
" let b = Complex{real: 3.0, imag: 4.0};\n",
|
||
"\n",
|
||
" let c = a.mul(b);\n",
|
||
" println!(\"Result: {} + {}i\", c.real, c.imag);\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Rust",
|
||
"language": "rust",
|
||
"name": "rust"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": "rust",
|
||
"file_extension": ".rs",
|
||
"mimetype": "text/rust",
|
||
"name": "Rust",
|
||
"pygment_lexer": "rust",
|
||
"version": ""
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 2
|
||
}
|