1 00:00:00,000 --> 00:00:07,200 [No Audio] 2 00:00:07,201 --> 00:00:09,666 When code involves generics, there needs to 3 00:00:09,667 --> 00:00:11,700 be a mechanism to determine which specific 4 00:00:11,701 --> 00:00:14,933 version is actually executed. For instance, a 5 00:00:14,934 --> 00:00:18,000 generic may assume a float value or an i32 6 00:00:18,001 --> 00:00:20,633 value, the compiler needs to somehow know 7 00:00:20,634 --> 00:00:23,100 what particular concrete types it will be 8 00:00:23,101 --> 00:00:25,900 converted to during the execution. This is 9 00:00:25,901 --> 00:00:28,766 called dispatch. There are two major forms of 10 00:00:28,767 --> 00:00:31,366 dispatch, static dispatch and dynamic 11 00:00:31,367 --> 00:00:34,066 dispatch. While Rust favors static 12 00:00:34,067 --> 00:00:36,833 dispatch, it also supports dynamic dispatch 13 00:00:36,866 --> 00:00:39,300 through a mechanism called trait objects. 14 00:00:39,766 --> 00:00:42,000 Let's start with a simple example and things 15 00:00:42,001 --> 00:00:45,000 will start to make some sense. Let us define 16 00:00:45,001 --> 00:00:47,166 a simple trait called print which will be 17 00:00:47,167 --> 00:00:49,300 used to print different types of values. 18 00:00:49,301 --> 00:00:54,333 [No Audio] 19 00:00:54,334 --> 00:00:56,333 This print will contain a single function 20 00:00:56,334 --> 00:00:59,100 called trait, which will accept a self type. 21 00:00:59,101 --> 00:01:04,700 [No Audio] 22 00:01:04,701 --> 00:01:07,200 Let us now implement this trait for a string value. 23 00:01:07,201 --> 00:01:12,166 [No Audio] 24 00:01:12,167 --> 00:01:14,033 The signature of the function should match 25 00:01:14,062 --> 00:01:16,133 the signature used inside the trait. 26 00:01:16,134 --> 00:01:20,233 [No Audio] 27 00:01:20,234 --> 00:01:22,200 Inside the function I will include a single 28 00:01:22,201 --> 00:01:24,700 statement where I will display the value of the string. 29 00:01:24,701 --> 00:01:30,566 [No Audio] 30 00:01:30,567 --> 00:01:33,800 Next, I will implement the same trait for an i32 value. 31 00:01:33,801 --> 00:01:38,933 [No Audio] 32 00:01:38,934 --> 00:01:40,666 Again, the signature of the function will 33 00:01:40,696 --> 00:01:42,962 match, to the signature inside the trait. 34 00:01:42,963 --> 00:01:48,700 [No Audio] 35 00:01:48,701 --> 00:01:50,866 Finally, we will include a print statement 36 00:01:50,867 --> 00:01:53,400 for displaying the contents of the value received. 37 00:01:53,401 --> 00:01:59,633 received. 38 00:01:59,634 --> 00:02:01,933 I will next define a simple display function 39 00:02:01,934 --> 00:02:04,600 with a generic T having a trait of print. 40 00:02:04,601 --> 00:02:09,466 received. 41 00:02:09,467 --> 00:02:11,800 This means now that T can be anything which 42 00:02:11,801 --> 00:02:14,933 has the print trait. In this case since print 43 00:02:14,934 --> 00:02:17,633 rates are only defined for string and i32 44 00:02:17,634 --> 00:02:20,400 values therefore, only these two types will 45 00:02:20,401 --> 00:02:22,900 be allowed inside the function I will call 46 00:02:22,901 --> 00:02:24,433 the print function on x. 47 00:02:24,434 --> 00:02:29,833 [No Audio] 48 00:02:29,834 --> 00:02:31,800 Let us now called the display function from 49 00:02:31,801 --> 00:02:35,000 the main using an i32 value and a string value. 50 00:02:35,001 --> 00:02:41,466 [No Audio] 51 00:02:41,467 --> 00:02:43,800 Now, what happened behind the scenes is that 52 00:02:43,801 --> 00:02:47,266 Rust uses something called as monomorphization. 53 00:02:47,267 --> 00:02:49,200 This means that Rust will 54 00:02:49,201 --> 00:02:51,900 create a special version of display for both 55 00:02:51,901 --> 00:02:54,466 i32 and string values, it will next 56 00:02:54,467 --> 00:02:56,966 replace the call sites with calls to these 57 00:02:56,967 --> 00:02:59,733 specialized functions. Let me write the code 58 00:02:59,734 --> 00:03:02,400 which more or less the Rust will generate. 59 00:03:02,401 --> 00:03:08,633 [No Audio] 60 00:03:08,634 --> 00:03:10,566 The first call will generate the specific 61 00:03:10,567 --> 00:03:12,900 version of the function with the type of T, 62 00:03:13,100 --> 00:03:15,566 being set to that of i32 and the second 63 00:03:15,567 --> 00:03:17,833 call will generate the specific version of 64 00:03:17,834 --> 00:03:20,233 the function with the type of T being set to 65 00:03:20,234 --> 00:03:24,100 that of the string type, this is called monomorphization. 66 00:03:24,101 --> 00:03:25,800 With monomorphization, the 67 00:03:25,801 --> 00:03:28,500 Rust performs a static dispatch which enables 68 00:03:28,501 --> 00:03:30,766 it to determine which specific versions are 69 00:03:30,800 --> 00:03:33,900 actually being executed. And in particular, 70 00:03:33,901 --> 00:03:36,333 the static dispatch enables the Rust resolves 71 00:03:36,334 --> 00:03:39,166 to a specific concrete type for generic T at 72 00:03:39,167 --> 00:03:41,766 compile time, the compiler creates a 73 00:03:41,767 --> 00:03:44,033 different versions of this function for each 74 00:03:44,034 --> 00:03:46,233 concrete type used to call it. T will 75 00:03:46,234 --> 00:03:48,733 represent a known size type after 76 00:03:48,734 --> 00:03:52,466 compilation. This, that is its size is known 77 00:03:52,500 --> 00:03:56,266 at compilation time. The static dispatch 78 00:03:56,267 --> 00:03:58,666 allows for inlining, and hence usually has 79 00:03:58,667 --> 00:04:01,500 higher performance. By inlining we mean the 80 00:04:01,501 --> 00:04:03,966 replacement of a function call at a call site 81 00:04:04,133 --> 00:04:06,400 with the body of the function. It however 82 00:04:06,401 --> 00:04:09,566 have some downsides, it generates many copies 83 00:04:09,567 --> 00:04:11,700 of the same function existing in the binary, 84 00:04:11,701 --> 00:04:14,466 one for each type. However, please note that 85 00:04:14,467 --> 00:04:16,966 it is a sort of an in demand in the sense 86 00:04:16,967 --> 00:04:19,100 that not all allowed versions will be 87 00:04:19,101 --> 00:04:21,200 created, but rather those versions are 88 00:04:21,201 --> 00:04:24,433 created, which are actually being used. In 89 00:04:24,434 --> 00:04:26,833 contrast to static dispatch there is another 90 00:04:26,834 --> 00:04:29,766 approach called dynamic dispatch. The static 91 00:04:29,767 --> 00:04:32,333 dispatch may sometimes become inefficient 92 00:04:32,334 --> 00:04:34,466 when it tends to make many copies of the same 93 00:04:34,467 --> 00:04:37,666 function. Rust provides dynamic dispatch 94 00:04:37,667 --> 00:04:40,033 through a feature called a trait objects. 95 00:04:40,100 --> 00:04:42,600 Simply put, a trait object is a reference to a 96 00:04:42,601 --> 00:04:45,600 trait. They are normal values that store a 97 00:04:45,601 --> 00:04:47,900 value of any type that implements the given 98 00:04:47,901 --> 00:04:51,066 trait, where the precise type can only be known 99 00:04:51,067 --> 00:04:54,000 at runtime. Since the type is not known at 100 00:04:54,001 --> 00:04:56,100 compile time, therefore, the specific 101 00:04:56,101 --> 00:04:59,266 versions are not been created. Let us see, 102 00:04:59,766 --> 00:05:02,266 let's see how to define a trait object 103 00:05:02,267 --> 00:05:03,966 with the help of an example, I will comment 104 00:05:03,967 --> 00:05:05,233 out the code first. 105 00:05:05,234 --> 00:05:09,201 [No Audio] 106 00:05:09,202 --> 00:05:10,266 Let us consider a couple 107 00:05:10,267 --> 00:05:12,433 of simple structs called Person and Students 108 00:05:12,434 --> 00:05:13,833 with a field of name. 109 00:05:13,834 --> 00:05:20,733 [No Audio] 110 00:05:20,734 --> 00:05:23,066 Next, we will define a trait called info, 111 00:05:23,067 --> 00:05:25,500 which will contain a single function of info. 112 00:05:25,501 --> 00:05:29,700 [No Audio] 113 00:05:29,701 --> 00:05:32,733 First we will implement the info trait for the Person. 114 00:05:32,734 --> 00:05:36,700 [No Audio] 115 00:05:36,722 --> 00:05:38,855 Inside the implementation, we 116 00:05:38,856 --> 00:05:40,566 will define the info function which will 117 00:05:40,567 --> 00:05:42,100 display the name. 118 00:05:42,101 --> 00:05:46,130 [No Audio] 119 00:05:46,131 --> 00:05:47,200 Next, we will implement the 120 00:05:47,201 --> 00:05:49,233 same trait for the student and will display 121 00:05:49,234 --> 00:05:51,142 his name inside the info function. 122 00:05:51,143 --> 00:06:03,100 [No Audio] 123 00:06:03,101 --> 00:06:05,400 Next, to clearly see the difference of static 124 00:06:05,401 --> 00:06:07,500 and dynamic dispatch, I will define a couple 125 00:06:07,501 --> 00:06:09,400 of functions, the first function will be 126 00:06:09,401 --> 00:06:11,300 called a static_dispatch. 127 00:06:11,301 --> 00:06:16,366 [No Audio] 128 00:06:16,367 --> 00:06:18,900 The input to the function is a reference to a generic 129 00:06:18,901 --> 00:06:21,866 type T, where T can be anything that implements info. 130 00:06:21,867 --> 00:06:26,366 [No Audio] 131 00:06:26,367 --> 00:06:27,700 Next, I'm going to define 132 00:06:27,707 --> 00:06:30,100 another function called dynamic_dispatch. 133 00:06:30,366 --> 00:06:31,933 The input to this function will be 134 00:06:31,966 --> 00:06:33,433 a reference to the trait info. 135 00:06:33,434 --> 00:06:39,733 [No Audio] 136 00:06:39,734 --> 00:06:42,366 This reference to a trait is called a trait 137 00:06:42,367 --> 00:06:44,500 object which is being specified using the 138 00:06:44,501 --> 00:06:47,900 keyword of dyn. A trait object is defined by 139 00:06:47,901 --> 00:06:50,500 having a reference to a trait. We will explain 140 00:06:50,501 --> 00:06:52,500 it in a second, but for now, let us complete 141 00:06:52,501 --> 00:06:54,333 the function by calling the info function 142 00:06:54,334 --> 00:06:55,577 inside the function. 143 00:06:55,578 --> 00:07:02,666 [No Audio] 144 00:07:02,667 --> 00:07:04,800 Okay, now, let us explain it in some detail. 145 00:07:05,366 --> 00:07:08,233 We know the type has traits, that is types are 146 00:07:08,234 --> 00:07:10,933 tied with some traits and traits are not tied 147 00:07:10,934 --> 00:07:13,366 with types. They rather define some 148 00:07:13,367 --> 00:07:15,266 functionality which may be followed by some 149 00:07:15,267 --> 00:07:18,033 types. Therefore, when a trait object is seen 150 00:07:18,034 --> 00:07:20,266 by Rust, it does not try to convert it to 151 00:07:20,267 --> 00:07:23,333 specific concrete types. Since that trait 152 00:07:23,366 --> 00:07:25,233 does not have types, and therefore the 153 00:07:25,234 --> 00:07:27,266 specific concrete versions are not being 154 00:07:27,267 --> 00:07:31,166 created at compile time. The end followed by 155 00:07:31,167 --> 00:07:32,866 dyn and then the name of the trait is 156 00:07:32,867 --> 00:07:34,833 basically the trait object. In this case. 157 00:07:35,333 --> 00:07:37,466 There's only one version of this function at 158 00:07:37,467 --> 00:07:39,833 runtime. So this reduces the size of the 159 00:07:39,834 --> 00:07:42,133 compiled program if the function is called 160 00:07:42,134 --> 00:07:44,633 with several different types versus using 161 00:07:44,634 --> 00:07:48,000 stating_dispatch. We can create 162 00:07:48,001 --> 00:07:49,933 the trait object using different pointers 163 00:07:49,934 --> 00:07:53,100 such as Box or Rc or Arc smart pointers by 164 00:07:53,101 --> 00:07:56,233 wrapping the info trait inside them with the 165 00:07:56,234 --> 00:07:59,000 dyn keyword, they are all more or less 166 00:07:59,033 --> 00:08:02,100 equivalent. And now let us use these two 167 00:08:02,101 --> 00:08:04,500 functions inside the main. First I will create 168 00:08:04,501 --> 00:08:06,766 a variable of type Person with a suitable name. 169 00:08:06,767 --> 00:08:12,500 [No Audio] 170 00:08:12,501 --> 00:08:14,666 Next, I will create another variable of type 171 00:08:14,700 --> 00:08:16,633 student with another suitable name. 172 00:08:16,634 --> 00:08:20,900 [No Audio] 173 00:08:20,901 --> 00:08:23,266 Let us now call the static_dispatch for 174 00:08:23,267 --> 00:08:24,400 the two variables. 175 00:08:24,401 --> 00:08:30,600 [No Audio] 176 00:08:30,601 --> 00:08:33,566 In the same way, I will call the dynamic_dispatch 177 00:08:33,567 --> 00:08:34,895 for the two variables. 178 00:08:34,896 --> 00:08:39,299 [No Audio] 179 00:08:39,328 --> 00:08:41,729 The static dispatch function can be 180 00:08:41,730 --> 00:08:44,500 called for any type that has the info trait. 181 00:08:44,733 --> 00:08:46,566 In the same way that dynamic dispatch can 182 00:08:46,567 --> 00:08:48,833 also be called for any type that has the info 183 00:08:48,834 --> 00:08:52,133 trait. The key difference is the input types 184 00:08:52,134 --> 00:08:54,600 to the two types of dispatches. The 185 00:08:54,601 --> 00:08:56,400 function with static_dispatch 186 00:08:56,401 --> 00:08:58,500 receives a generic type which is resolved 187 00:08:58,501 --> 00:09:00,666 into concrete types. While the dynamic 188 00:09:00,667 --> 00:09:03,400 dispatch function has straight objects. In 189 00:09:03,401 --> 00:09:06,366 particular, it obtains a trait object plus a 190 00:09:06,367 --> 00:09:09,066 table with all the methods that are defined 191 00:09:09,067 --> 00:09:11,533 on the trait. Calling a specific function on 192 00:09:11,534 --> 00:09:13,833 the object will mean to look up, to look it up 193 00:09:13,834 --> 00:09:15,966 in the table and then executing the code. 194 00:09:17,466 --> 00:09:19,866 This will take some time to look and identify 195 00:09:19,900 --> 00:09:21,900 and then retrieve the code of the function. 196 00:09:22,233 --> 00:09:24,900 In contrast, static dispatch does not involve 197 00:09:24,933 --> 00:09:27,200 this lookup and is therefore comparatively 198 00:09:27,201 --> 00:09:30,433 faster. Now let's cover a nice use case of 199 00:09:30,434 --> 00:09:32,733 the trait objects or dynamic dispatch. 200 00:09:33,600 --> 00:09:35,933 Earlier in this course, we covered an example 201 00:09:35,934 --> 00:09:37,966 where we pointed out a solution to a key 202 00:09:37,967 --> 00:09:39,933 limitation of vectors, which is that they 203 00:09:39,934 --> 00:09:42,600 allow only elements of a single type. We 204 00:09:42,601 --> 00:09:45,300 created a workaround, a workaround where we 205 00:09:45,301 --> 00:09:48,233 defined an enum which having two variants for 206 00:09:48,234 --> 00:09:50,966 holding integer and float types. Now, 207 00:09:51,000 --> 00:09:53,333 defining a vector of the enum type allows us 208 00:09:53,334 --> 00:09:55,266 to store different types of data in the same 209 00:09:55,267 --> 00:09:57,933 vector. This is perfectly good solution when 210 00:09:57,934 --> 00:09:59,966 our different types are off limits. 211 00:10:00,000 --> 00:10:02,933 accountable types that we know when our code 212 00:10:02,934 --> 00:10:05,733 is being compiled. However, sometimes we 213 00:10:05,734 --> 00:10:08,033 encounter a situation where our types may 214 00:10:08,034 --> 00:10:09,766 increase with the passage of time and 215 00:10:09,767 --> 00:10:11,566 therefore, we need to keep room for those 216 00:10:11,567 --> 00:10:14,100 types which may evolve later on with respect 217 00:10:14,101 --> 00:10:16,800 to time. This means we need to have some 218 00:10:16,801 --> 00:10:19,266 flexibility in our code, so that it can 219 00:10:19,300 --> 00:10:22,666 easily adapt to new evolving types. The 220 00:10:22,667 --> 00:10:24,700 dynamic display provides a perfect solution 221 00:10:24,701 --> 00:10:27,066 in this regards, I will comment out the code 222 00:10:27,067 --> 00:10:28,366 and we'll start fresh again. 223 00:10:28,367 --> 00:10:32,566 [No Audio] 224 00:10:32,567 --> 00:10:34,941 I will use the same program that we wrote earlier. 225 00:10:34,942 --> 00:10:36,366 So let me paste it. 226 00:10:36,367 --> 00:10:39,418 [No Audio] 227 00:10:39,419 --> 00:10:41,400 This program was based on a print 228 00:10:41,401 --> 00:10:43,966 trait which contains a print function, we 229 00:10:43,967 --> 00:10:46,166 have two types of i32 and string which 230 00:10:46,167 --> 00:10:48,600 implements this trait. Finally, we have a 231 00:10:48,601 --> 00:10:51,500 display function, which which accepts any 232 00:10:51,501 --> 00:10:53,933 type which implements the print trait and is 233 00:10:53,934 --> 00:10:56,200 used to call the print function. Now, from 234 00:10:56,201 --> 00:10:57,866 the previous discussion, we know that this 235 00:10:57,867 --> 00:10:59,504 function is doing a static dispatch. 236 00:10:59,505 --> 00:11:00,800 So, let me rename it. 237 00:11:00,801 --> 00:11:04,262 [No Audio] 238 00:11:04,263 --> 00:11:05,700 In the main I will change the 239 00:11:05,701 --> 00:11:07,666 name of the function accordingly in the call 240 00:11:07,667 --> 00:11:08,933 to the function. 241 00:11:08,934 --> 00:11:13,320 [No Audio] 242 00:11:13,321 --> 00:11:14,700 Now, what I want is to pass 243 00:11:14,701 --> 00:11:16,900 a vector containing different types to the 244 00:11:16,901 --> 00:11:18,900 function and get the values to be displayed. 245 00:11:19,166 --> 00:11:21,700 Since the generic can assume a single type 246 00:11:21,701 --> 00:11:23,933 therefore, the static dispatch will not work 247 00:11:23,934 --> 00:11:26,200 here. This is because the generic will be 248 00:11:26,201 --> 00:11:28,366 resolved to a single concrete type during the 249 00:11:28,367 --> 00:11:31,233 compilation, this can be done using the 250 00:11:31,234 --> 00:11:33,700 dynamic dispatch, I will define another 251 00:11:33,701 --> 00:11:35,600 display function which will essentially do 252 00:11:35,601 --> 00:11:38,000 the same but using the dynamic dispatch. 253 00:11:38,001 --> 00:11:43,033 [No Audio] 254 00:11:43,034 --> 00:11:44,966 In the input to the function, I will mention a 255 00:11:44,967 --> 00:11:47,333 vector of trait objects since I want to pass 256 00:11:47,334 --> 00:11:49,600 in a vector containing different types to this function. 257 00:11:49,601 --> 00:11:56,300 [No Audio] 258 00:11:56,301 --> 00:11:58,466 This now means that x is a vector of any 259 00:11:58,467 --> 00:12:01,866 types that has the trait print. In the body of 260 00:12:01,867 --> 00:12:03,566 the function I will iterate through all the 261 00:12:03,567 --> 00:12:05,900 values of the vector x, and will display its values. 262 00:12:05,901 --> 00:12:12,500 [No Audio] 263 00:12:12,501 --> 00:12:14,433 In the main now, I can call this function 264 00:12:14,434 --> 00:12:16,333 with a vector containing different types. 265 00:12:16,334 --> 00:12:21,600 [No Audio] 266 00:12:21,601 --> 00:12:23,166 Please note that we need to pass in 267 00:12:23,167 --> 00:12:25,933 references to values and not concrete values. 268 00:12:26,100 --> 00:12:27,533 Let us cargo run this. 269 00:12:27,534 --> 00:12:31,731 [No Audio] 270 00:12:31,732 --> 00:12:33,033 You may note that the compiler 271 00:12:33,034 --> 00:12:34,800 has no issues. This is one of the 272 00:12:34,801 --> 00:12:37,200 main use cases of trait objects, that is, it 273 00:12:37,201 --> 00:12:39,333 allows for defining vectors of having 274 00:12:39,334 --> 00:12:41,166 different values, with the possibility of 275 00:12:41,167 --> 00:12:43,200 extending the types inside the vector in 276 00:12:43,201 --> 00:12:46,200 future. Earlier we mentioned that a trait 277 00:12:46,201 --> 00:12:49,233 objects are reference to traits and they can 278 00:12:49,234 --> 00:12:51,500 be created using either a simple reference or 279 00:12:51,501 --> 00:12:55,333 Box an Arc or Rc smart pointers. So let us 280 00:12:55,334 --> 00:12:57,933 redefine the function display_dynamic 281 00:12:57,934 --> 00:12:59,833 using the Box smart pointer. 282 00:13:00,600 --> 00:13:03,266 I will write the dyn print inside a Box and we'll 283 00:13:03,267 --> 00:13:04,700 delete the simple reference. 284 00:13:04,701 --> 00:13:09,033 [No Audio] 285 00:13:09,034 --> 00:13:11,452 In the call to the function in the main program, 286 00:13:11,453 --> 00:13:13,900 I will write the values of the vector inside the Box, 287 00:13:13,901 --> 00:13:15,600 and we'll delete the references. 288 00:13:15,601 --> 00:13:19,327 [No Audio] 289 00:13:19,328 --> 00:13:21,133 You may note that this compiles fine 290 00:13:21,266 --> 00:13:22,700 in the same way we can use 291 00:13:22,701 --> 00:13:24,900 the Arc or Rc smart pointers to create the 292 00:13:24,901 --> 00:13:28,400 trait objects. Okay, in summary, it is more 293 00:13:28,401 --> 00:13:30,633 efficient to use static dispatch when the 294 00:13:30,634 --> 00:13:33,833 underlying types are not many numbers. The 295 00:13:33,834 --> 00:13:36,333 standard library also generally tries to 296 00:13:36,334 --> 00:13:38,500 statically dispatched wherever possible. 297 00:13:38,733 --> 00:13:41,366 However, when the types involved are many, we 298 00:13:41,367 --> 00:13:43,966 would like to do a dynamic dispatch. The 299 00:13:43,967 --> 00:13:46,033 static dispatch is made possible with the 300 00:13:46,034 --> 00:13:48,633 help of the monomorphization, while the 301 00:13:48,634 --> 00:13:50,500 dynamic dispatch is made possible with the 302 00:13:50,501 --> 00:13:53,200 help of trait objects. With this we 303 00:13:53,201 --> 00:13:55,200 end this tutorial see you again with 304 00:13:55,201 --> 00:13:58,233 more, and until next tutorial happy Rust programming. 305 00:13:58,234 --> 00:14:03,966 [No Audio]