1 00:00:01,540 --> 00:00:02,410 In this module, 2 00:00:02,420 --> 00:00:09,559 we're going to go into detail about the slice data type. In unit 1, module 4, we briefly mentioned the slice 3 00:00:09,560 --> 00:00:12,300 primitive data type. 4 00:00:12,430 --> 00:00:13,890 Slices borrow data. So, now that you know about borrowing, 5 00:00:13,950 --> 00:00:21,100 we can cover slices more thoroughly. In this module, we'll talk about what a slice is and how to create them. 6 00:00:21,640 --> 00:00:26,350 We'll show why it's a good idea to use slices as parameter types in functions or methods. 7 00:00:26,840 --> 00:00:33,750 And we'll discuss how we were able to borrow a String in the last module and pass that to a function with a string slice as a parameter type. 8 00:00:35,240 --> 00:00:37,070 So, what is a slice? 9 00:00:38,540 --> 00:00:43,260 A slice is a data type that always borrows data owned by some other data structure, 10 00:00:43,440 --> 00:00:45,980 such as the String type that we covered in module 2. 11 00:00:46,990 --> 00:00:49,310 A slice consists of a pointer and a length. 12 00:00:50,340 --> 00:00:53,790 The pointer is a reference to the start of the data that the slice contains, 13 00:00:54,700 --> 00:00:58,880 and the length is the number of elements after the start that the slice contains. 14 00:01:00,080 --> 00:01:03,350 This slice contains the two ls from the hello string. 15 00:01:04,840 --> 00:01:07,060 Now that we know how a slice is stored, 16 00:01:07,210 --> 00:01:09,350 let's look at how to create a slice in code. 17 00:01:10,840 --> 00:01:14,950 Even though a slice is stored as a pointer and a length, in code, 18 00:01:15,120 --> 00:01:17,290 we create a slice using an ampersand, 19 00:01:17,590 --> 00:01:23,710 the variable we're referencing, and square brackets containing a range. 20 00:01:23,770 --> 00:01:26,569 In this case, the starting index is 2, and the ending index is 21 00:01:26,570 --> 00:01:27,250 4. 22 00:01:28,740 --> 00:01:31,320 If the slice you create starts at index 0, 23 00:01:31,580 --> 00:01:33,130 the starting index is optional, 24 00:01:33,190 --> 00:01:35,890 and you can just put two dots and the ending index. 25 00:01:36,740 --> 00:01:39,740 If the slice you create goes all the way to the end of the data structure, 26 00:01:40,100 --> 00:01:41,750 the ending index is optional. 27 00:01:42,740 --> 00:01:44,850 To make a slice that borrows all of the data, 28 00:01:45,240 --> 00:01:47,910 all you need inside the square brackets are two dots. 29 00:01:49,440 --> 00:01:53,919 We can also create slices from arrays and vectors. Recall from unit 1, module 30 00:01:53,920 --> 00:01:58,550 4, that arrays are fixed-length collections where all of the elements have the same type, 31 00:01:59,000 --> 00:02:01,450 such as this array of f64 values. 32 00:02:02,440 --> 00:02:07,950 We also mentioned, in that module, that vectors are similar to arrays but can grow or shrink in size, 33 00:02:08,270 --> 00:02:17,650 such as this vector of i32 values that we can add an element onto. Creating a slice from an array or vector is similar to creating a slice from a string: 34 00:02:18,240 --> 00:02:24,130 use an ampersand, then a range and square brackets. 35 00:02:24,190 --> 00:02:28,530 Like plain references, the borrow checker ensures slices are always valid. At runtime, 36 00:02:28,730 --> 00:02:30,730 Rust will panic and stop your program 37 00:02:30,750 --> 00:02:33,010 if slice indices are out of bounds. 38 00:02:33,530 --> 00:02:35,250 These protections prevent bugs. 39 00:02:36,790 --> 00:02:39,920 Let's look an example of trying to use invalid indices. 40 00:02:40,540 --> 00:02:41,430 In this program, 41 00:02:41,440 --> 00:02:43,430 we have a vector containing three elements, 42 00:02:43,440 --> 00:02:46,050 and we try to create a slice up to index 9. 43 00:02:47,040 --> 00:02:51,720 Even though we, as humans, can see that index 9 is going to be out of bounds in this example, 44 00:02:51,950 --> 00:02:55,250 Rust does not analyze the index with the slice when it compiles the code. 45 00:02:55,840 --> 00:03:00,149 If we had used a value provided by the user of the program rather than hardcoding the index, 46 00:03:00,150 --> 00:03:00,960 as we have here, 47 00:03:01,280 --> 00:03:04,050 Rust wouldn't even be able to do an analysis at compile time. 48 00:03:05,690 --> 00:03:07,760 This code will compile without any errors. 49 00:03:07,990 --> 00:03:10,510 Slice indices are not analyzed at compile time. 50 00:03:11,140 --> 00:03:11,780 However, 51 00:03:11,870 --> 00:03:13,040 when we run this program, 52 00:03:13,220 --> 00:03:17,170 it panics with the error index 9 out of range for slice of length 3. 53 00:03:20,040 --> 00:03:23,370 Because Rust can't prove the slice indices are valid at compile time, 54 00:03:23,590 --> 00:03:27,500 it checks the slice indices at runtime and deliberately terminates the program 55 00:03:27,510 --> 00:03:28,500 if they're invalid. 56 00:03:29,140 --> 00:03:31,040 This prevents the use of invalid memory, 57 00:03:31,250 --> 00:03:33,750 which would lead to seg faults or undefined behavior. 58 00:03:35,280 --> 00:03:37,330 String slices having an additional protection. 59 00:03:37,730 --> 00:03:42,100 The indices of the slice range must be at valid Unicode character boundaries. 60 00:03:43,620 --> 00:03:44,330 In this code, 61 00:03:44,520 --> 00:03:46,580 we have a string literal containing emoji, 62 00:03:46,750 --> 00:03:48,550 each of which use multiple bytes. 63 00:03:49,340 --> 00:03:51,950 We then attempt to create a slice from 0 to 1. 64 00:03:53,440 --> 00:03:55,739 This code also compiles. 65 00:03:55,740 --> 00:03:57,479 Character boundaries aren't checked at compile time 66 00:03:57,480 --> 00:03:57,950 either. 67 00:03:58,940 --> 00:04:00,010 When we run the program, 68 00:04:00,080 --> 00:04:02,229 it panics with the error message 69 00:04:02,230 --> 00:04:07,580 byte index 1 is not a char boundary. To ensure your program doesn't panic, 70 00:04:07,860 --> 00:04:11,360 consider using methods on String, like chars or char_indices, 71 00:04:11,600 --> 00:04:16,300 rather than slicing at arbitrary indices. 72 00:04:16,310 --> 00:04:23,670 In general, it's better to use a slice or a string slice as parameters to functions or methods, rather than borrowing a Vec, array, or String. 73 00:04:23,970 --> 00:04:27,320 Let's look at why. This function, 74 00:04:27,330 --> 00:04:29,270 named only_reference_to_array, 75 00:04:29,360 --> 00:04:34,010 has a parameter with the type of a reference to an array of three i32 values. 76 00:04:34,220 --> 00:04:36,750 That's the only type that this function will accept. 77 00:04:38,240 --> 00:04:39,120 This function, 78 00:04:39,130 --> 00:04:41,110 named only_reference_to_vector, 79 00:04:41,320 --> 00:04:42,820 also borrows its parameter. 80 00:04:43,060 --> 00:04:48,250 But this function will only accept a reference to a vector containing some number of i32 values. 81 00:04:49,840 --> 00:04:50,800 In contrast, 82 00:04:50,810 --> 00:04:51,580 this function, 83 00:04:51,590 --> 00:04:54,080 named reference_to_either_array_or_vector, 84 00:04:54,140 --> 00:04:56,770 has a slice of i32 values as its parameter. 85 00:04:57,090 --> 00:05:02,560 This function can be called with some number of i32 values borrowed from either an array or a vector. 86 00:05:02,890 --> 00:05:05,200 It can even be called with a slice of a slice. 87 00:05:06,740 --> 00:05:09,460 As you can see from this usage of each of the functions, 88 00:05:09,680 --> 00:05:15,850 using a slice as a parameter gives callers more flexibility and means functions can be used in more contexts. 89 00:05:17,240 --> 00:05:18,150 Similarly, 90 00:05:18,490 --> 00:05:22,820 by specifying string slices as parameters rather than borrowing an owned String, 91 00:05:23,170 --> 00:05:26,650 functions can accept either borrowed strings or string literals. 92 00:05:28,140 --> 00:05:30,560 String literals create string slices. 93 00:05:30,810 --> 00:05:33,270 Their text appears in your compiled program, 94 00:05:33,380 --> 00:05:35,850 which is also stored in memory, just like your data. 95 00:05:36,640 --> 00:05:39,250 The string slice points to this literal text. 96 00:05:40,740 --> 00:05:41,490 Finally, 97 00:05:41,500 --> 00:05:48,350 you may have been wondering how we're able to call functions that take a string slice by passing an argument that is a reference to an owned String. 98 00:05:49,940 --> 00:05:54,650 This is due to a combination of features Rust provides to make using slices more ergonomic. 99 00:05:55,440 --> 00:05:59,710 We're going to discuss how this works at a high level. 100 00:05:59,760 --> 00:06:04,140 First, the Standard Library includes the implementation of a trait called Deref on String, 101 00:06:04,290 --> 00:06:06,070 such that Rust knows how to convert 102 00:06:06,080 --> 00:06:10,300 a reference to a String into a string slice containing the whole String. 103 00:06:11,540 --> 00:06:12,120 Next, 104 00:06:12,400 --> 00:06:15,050 Rust has a feature called deref coercion. 105 00:06:15,490 --> 00:06:17,700 This means that when you call a function or method, 106 00:06:17,890 --> 00:06:20,530 the compiler will automatically dereference the arguments, 107 00:06:20,650 --> 00:06:21,440 if need be, 108 00:06:21,600 --> 00:06:24,150 to convert them to match the function parameter type. 109 00:06:25,690 --> 00:06:29,250 This is why we were able to call the pluralize function in the last module, 110 00:06:29,640 --> 00:06:32,230 even though it took a string slice as its parameter type, 111 00:06:32,400 --> 00:06:35,350 and we had an argument of a reference to an owned String. 112 00:06:36,770 --> 00:06:38,329 This works for arrays and vectors 113 00:06:38,330 --> 00:06:38,850 too. 114 00:06:39,240 --> 00:06:40,030 For example, 115 00:06:40,340 --> 00:06:40,669 earlier, 116 00:06:40,670 --> 00:06:43,260 when we called the either_array_or_vector function, 117 00:06:43,600 --> 00:06:51,550 we actually didn't need the square brackets and the range. A reference to the array or vector coerces to a slice of the whole array or vector. 118 00:06:52,940 --> 00:06:56,610 See the Deref traits documentation for other implementations. 119 00:06:58,100 --> 00:06:59,010 In this module, 120 00:06:59,070 --> 00:07:03,650 you learned that a slice is a way to borrow a contiguous chunk of data owned elsewhere. 121 00:07:04,740 --> 00:07:07,000 You know how to create a slice using an ampersand, 122 00:07:07,170 --> 00:07:08,950 square brackets, and a range. 123 00:07:09,940 --> 00:07:21,550 We demonstrated that using slices as parameters makes functions more flexible, and how the Deref trait and deref coercion combine to make slices ergonomic to use in common situations. 124 00:07:22,700 --> 00:07:23,670 In the next module, 125 00:07:23,770 --> 00:07:26,410 we'll talk about how borrowing interacts with mutability.