1 00:00:06,570 --> 00:00:08,460 - In this section, we're going to see how to use 2 00:00:08,460 --> 00:00:12,330 type inference to simplify closure syntax. 3 00:00:12,330 --> 00:00:13,950 So first of all, a quick recap 4 00:00:13,950 --> 00:00:16,500 of explicit typing with closures. 5 00:00:16,500 --> 00:00:19,230 Have a look at this simple closure here. 6 00:00:19,230 --> 00:00:20,640 Note the following points. 7 00:00:20,640 --> 00:00:23,070 We specified type info explicitly 8 00:00:23,070 --> 00:00:26,160 for the parameters and for the return type. 9 00:00:26,160 --> 00:00:28,290 And also the body of the closure 10 00:00:28,290 --> 00:00:30,300 is enclosed inside curly brackets. 11 00:00:30,300 --> 00:00:32,610 So this is full syntax for closures, 12 00:00:32,610 --> 00:00:35,730 but we can simplify the syntax quite a bit actually. 13 00:00:35,730 --> 00:00:38,700 So you can omit the parameter types. 14 00:00:38,700 --> 00:00:40,650 Rust will infer the parameter types 15 00:00:40,650 --> 00:00:42,660 based on the values you pass in 16 00:00:42,660 --> 00:00:44,940 the first time that you call the closure. 17 00:00:44,940 --> 00:00:47,100 You can also omit the return type. 18 00:00:47,100 --> 00:00:49,950 Rust will infer the return type based on 19 00:00:49,950 --> 00:00:52,770 how the algorithm goes inside the closure to see 20 00:00:52,770 --> 00:00:56,160 what type of value you've actually returned in practice. 21 00:00:56,160 --> 00:00:59,850 So here is a simpler closure, get_timestamp. 22 00:00:59,850 --> 00:01:03,000 It's a closure, it doesn't take any parameters, 23 00:01:03,000 --> 00:01:05,250 but we haven't specified the return type 24 00:01:05,250 --> 00:01:08,400 and we haven't specified curly brackets either. 25 00:01:08,400 --> 00:01:10,560 Okay, so we have simple syntax. 26 00:01:10,560 --> 00:01:13,500 The return type isn't specified explicitly. 27 00:01:13,500 --> 00:01:15,660 It can figure it out for itself 28 00:01:15,660 --> 00:01:17,940 based on what it sees we've returned. 29 00:01:17,940 --> 00:01:22,050 And also we've omitted the curly brackets 30 00:01:22,050 --> 00:01:23,460 around the body of the closure. 31 00:01:23,460 --> 00:01:25,590 So it all looks a bit neater. 32 00:01:25,590 --> 00:01:28,530 So in this case, Rust will infer, 33 00:01:28,530 --> 00:01:30,900 it'll guess, it'll see clearly 34 00:01:30,900 --> 00:01:34,620 that the closure returns a DateTime of UTC. 35 00:01:34,620 --> 00:01:36,180 That's what this function returns. 36 00:01:36,180 --> 00:01:39,090 When you call the now function on UTC, 37 00:01:39,090 --> 00:01:43,710 it actually returns a DateTime used in the UTC time zone. 38 00:01:43,710 --> 00:01:47,400 So you can omit parameter types and return type 39 00:01:47,400 --> 00:01:49,410 and you can omit the curly brackets 40 00:01:49,410 --> 00:01:50,760 if it's a simple expression. 41 00:01:50,760 --> 00:01:52,800 And that's what people would tend to do. 42 00:01:52,800 --> 00:01:54,270 So here's another example. 43 00:01:54,270 --> 00:01:57,120 This closure takes a parameter, n. 44 00:01:57,120 --> 00:01:59,520 We haven't specified the type of n, 45 00:01:59,520 --> 00:02:03,210 so Rust will infer the type of the parameter 46 00:02:03,210 --> 00:02:05,610 the first time we call the closure. 47 00:02:05,610 --> 00:02:08,040 So it'll note the closure here. 48 00:02:08,040 --> 00:02:10,350 At the point when we define the closure, 49 00:02:10,350 --> 00:02:12,930 it doesn't yet know what type n is going to be, 50 00:02:12,930 --> 00:02:16,770 but when we call the closure, it basically binds n 51 00:02:16,770 --> 00:02:18,480 to be the parameter that we've passed in. 52 00:02:18,480 --> 00:02:20,970 So at this point, at the point of the call, 53 00:02:20,970 --> 00:02:22,530 the compiler will realize 54 00:02:22,530 --> 00:02:25,920 that the parameter is a 64-bit float. 55 00:02:25,920 --> 00:02:30,330 And then while the return value will also be a 64-bit float 56 00:02:30,330 --> 00:02:33,183 because you can see the expressions that we've got. 57 00:02:34,470 --> 00:02:37,260 So once you've called a function 58 00:02:37,260 --> 00:02:40,890 and you've bound the parameter to a particular type, 59 00:02:40,890 --> 00:02:42,960 you've gotta be consistent, okay? 60 00:02:42,960 --> 00:02:44,880 So it is not like it'll regenerate 61 00:02:44,880 --> 00:02:47,250 the closure for each call. 62 00:02:47,250 --> 00:02:48,720 Once you've invoked the closure, 63 00:02:48,720 --> 00:02:51,450 at that point, the compiler has decided 64 00:02:51,450 --> 00:02:54,480 that this is going to be a 64-bit float. 65 00:02:54,480 --> 00:02:57,750 So if you try to call it again a second time 66 00:02:57,750 --> 00:03:00,570 with a different type, you'd get an error 67 00:03:00,570 --> 00:03:04,350 because the closure now takes a 64-bit float 68 00:03:04,350 --> 00:03:06,570 and we've passed in an integer instead. 69 00:03:06,570 --> 00:03:09,570 It's the wrong type for what the compiler thinks it needs. 70 00:03:09,570 --> 00:03:12,510 So the first call is the important one. 71 00:03:12,510 --> 00:03:15,000 That's where the compiler makes the decision 72 00:03:15,000 --> 00:03:16,170 about what type it's going to be. 73 00:03:16,170 --> 00:03:18,470 And you have to then be consistent thereafter. 74 00:03:19,950 --> 00:03:23,190 Right, well, let's see an example in the demo project, 75 00:03:23,190 --> 00:03:24,353 lesson09_nested_functions_closures. 76 00:03:25,620 --> 00:03:28,830 We'll have a look at demo_closures_inferred_types 77 00:03:28,830 --> 00:03:29,790 and then we'll run it. 78 00:03:29,790 --> 00:03:32,040 Let's have a look at the code. 79 00:03:32,040 --> 00:03:35,890 Right, so in main, let me uncomment 80 00:03:37,080 --> 00:03:38,680 the demo_closures_inferred_types 81 00:03:41,250 --> 00:03:43,440 and let's have a look at it. 82 00:03:43,440 --> 00:03:46,680 So in here, I've got a few functions 83 00:03:46,680 --> 00:03:50,400 that illustrate closures with no parameters, 84 00:03:50,400 --> 00:03:52,560 with one parameter, with many parameters, 85 00:03:52,560 --> 00:03:54,780 and with multiple statements. 86 00:03:54,780 --> 00:03:56,253 And here's the code. 87 00:03:57,240 --> 00:04:01,620 So a closure with no parameters and explicitly, 88 00:04:01,620 --> 00:04:04,800 implicitly infer in the return type. 89 00:04:04,800 --> 00:04:07,260 So when we call it, it knows that it returns 90 00:04:07,260 --> 00:04:11,610 a DateTime UTC, which we can then format as the time. 91 00:04:11,610 --> 00:04:13,800 And here's the second closure. 92 00:04:13,800 --> 00:04:16,860 At the point here, the compiler doesn't know yet 93 00:04:16,860 --> 00:04:19,590 what type n is going to be, but then when it sees 94 00:04:19,590 --> 00:04:22,110 that we've called it with a float 64, 95 00:04:22,110 --> 00:04:25,233 at that point, the compiler will realize it's a float 64. 96 00:04:26,370 --> 00:04:29,190 Therefore, if we try to call it a second time 97 00:04:29,190 --> 00:04:33,210 with some other type, we'd get an error on this line. 98 00:04:33,210 --> 00:04:35,774 So I'm just gonna prove the point. 99 00:04:35,774 --> 00:04:38,730 I'm gonna do a cargo check of the syntax 100 00:04:38,730 --> 00:04:41,850 and I think I'm gonna get an error here on line 28. 101 00:04:41,850 --> 00:04:42,683 Let's see. 102 00:04:47,531 --> 00:04:49,320 Right, okay. 103 00:04:49,320 --> 00:04:53,640 So it says we have mismatched types. 104 00:04:53,640 --> 00:04:58,380 Right, on line 28, the call the reciprocal, it was expecting 105 00:04:58,380 --> 00:05:01,470 a floating point number and it found an integer. 106 00:05:01,470 --> 00:05:03,870 In other words, the arguments to this function call 107 00:05:03,870 --> 00:05:08,160 are incorrect because the closure, 108 00:05:08,160 --> 00:05:12,720 it has already inferred the type to be a float 64 109 00:05:12,720 --> 00:05:16,350 and that's not a float 64. 110 00:05:16,350 --> 00:05:19,470 So the point when you define the closure, 111 00:05:19,470 --> 00:05:21,480 the type isn't known yet for the parameter, 112 00:05:21,480 --> 00:05:23,130 but when you call the function, at that point, 113 00:05:23,130 --> 00:05:26,910 it binds the type to be float 64, okay? 114 00:05:26,910 --> 00:05:29,670 So n must be float 64. 115 00:05:29,670 --> 00:05:33,750 I could have passed in 5.0, but I can't pass in five. 116 00:05:33,750 --> 00:05:37,560 Remember, Rust doesn't do automatic type conversions. 117 00:05:37,560 --> 00:05:39,420 So that statement wouldn't work. 118 00:05:39,420 --> 00:05:40,650 And then what have we got here? 119 00:05:40,650 --> 00:05:42,570 So I've got a closure that takes in 120 00:05:42,570 --> 00:05:44,430 a couple of parameters and it returns the product. 121 00:05:44,430 --> 00:05:47,100 Look how tidy that is, much easier. 122 00:05:47,100 --> 00:05:52,100 We don't have to say a is an i32 and b is an i32. 123 00:05:53,940 --> 00:05:57,450 Oh, and it returns an i32, 124 00:05:57,450 --> 00:05:59,610 and here's the body of the lambda. 125 00:05:59,610 --> 00:06:01,980 That's the kind of syntax we had before. 126 00:06:01,980 --> 00:06:04,500 But when we call the closure, 127 00:06:04,500 --> 00:06:07,350 the compiler can see that those are 32-bit integers. 128 00:06:07,350 --> 00:06:08,370 We don't need to tell it. 129 00:06:08,370 --> 00:06:10,140 It can see that clearly. 130 00:06:10,140 --> 00:06:12,780 So it knows that a and b are 32-bit integers. 131 00:06:12,780 --> 00:06:15,150 And if we return a multiplied by b, 132 00:06:15,150 --> 00:06:17,470 then clearly that is also an i32 133 00:06:18,390 --> 00:06:20,880 and we don't need the curly brackets either. 134 00:06:20,880 --> 00:06:22,500 Okay, so in practice, the syntax 135 00:06:22,500 --> 00:06:25,620 for closures is really quite elegant, I think. 136 00:06:25,620 --> 00:06:27,990 And then finally, this example, 137 00:06:27,990 --> 00:06:31,530 a multi-statement lambda or closure, I should say. 138 00:06:31,530 --> 00:06:35,460 When we call a function, we pass in the value five, okay? 139 00:06:35,460 --> 00:06:37,320 So that's the number of seconds. 140 00:06:37,320 --> 00:06:39,480 That's a 32-bit integer. 141 00:06:39,480 --> 00:06:41,700 You can still have curly brackets 142 00:06:41,700 --> 00:06:43,770 if you want to perform multiple statements. 143 00:06:43,770 --> 00:06:46,290 And then finally have an expression at the end 144 00:06:46,290 --> 00:06:47,123 and that's the return. 145 00:06:47,123 --> 00:06:49,080 And remember, if you have an expression 146 00:06:49,080 --> 00:06:51,510 at the end of a lambda or closure or function, 147 00:06:51,510 --> 00:06:54,660 it's as if it was an explicit return statement. 148 00:06:54,660 --> 00:06:58,140 So you could say return statement, but in practice, 149 00:06:58,140 --> 00:07:01,173 you'd probably just say expression like so. 150 00:07:02,130 --> 00:07:04,290 Okay, so all that remains is for me 151 00:07:04,290 --> 00:07:07,920 to actually run it to confirm that it does indeed work. 152 00:07:07,920 --> 00:07:08,753 Hope so. 153 00:07:11,220 --> 00:07:14,790 So I've got a five second delay in one of my demos 154 00:07:14,790 --> 00:07:17,490 so that's why it's pausing a little bit. 155 00:07:17,490 --> 00:07:20,580 So there we go, all the functions have worked nicely. 156 00:07:20,580 --> 00:07:23,790 So in practice, when you are using closures, 157 00:07:23,790 --> 00:07:27,210 you will use this abbreviated syntax 158 00:07:27,210 --> 00:07:30,273 to give you a more elegant approach.