0:00:04.080,0:00:08.560 [ applause ] 0:00:10.360,0:00:12.460 Yeah, I'm glad so many people[br]still showed up 0:00:12.460,0:00:15.000 at the very end of the conference. 0:00:15.000,0:00:19.980 So yeah, I'm going to be talking about[br]mostly a number of features 0:00:19.980,0:00:22.680 that were part of the Rust language[br]at some point, 0:00:22.680,0:00:24.340 but no longer are. 0:00:24.840,0:00:29.340 And, why this is the case[br]and usually why this is a good thing. 0:00:30.100,0:00:32.800 So, as Ryan said, I'm Marijn Haverbeke. 0:00:32.800,0:00:36.890 If you are into Javascript,[br]you might have seen my name before. 0:00:41.560,0:00:44.100 Rust has been under development[br]for a while: 0:00:44.100,0:00:45.740 about ten years now, I think. 0:00:45.740,0:00:50.760 First - long stretch - was just[br]Graydon working in isolation and... 0:00:51.140,0:00:55.020 Who knows what kind of ideas and[br]experiments he abandoned at that point. 0:00:55.020,0:00:59.400 There's not even a like code repository[br]from that time public, 0:00:59.400,0:01:01.840 so it's like the prehistory. 0:01:02.000,0:01:03.660 And then, at some point, 0:01:03.660,0:01:07.740 Mozilla adopted the project[br]and assembled a team. 0:01:09.300,0:01:12.020 From a little before that point,[br]we do have Git history, 0:01:12.020,0:01:14.940 and at that point,[br]everything was discussed in issues 0:01:14.940,0:01:17.020 and mailing lists, and there's... 0:01:17.620,0:01:19.130 lots of records. 0:01:19.130,0:01:23.360 I was part of this pretty much -[br]the initial team - where we... 0:01:24.060,0:01:28.620 moved from the original OCaml-based[br]compiler to a Rust-based compiler, 0:01:28.620,0:01:31.680 and we got a bunch more people[br]involved in the language. 0:01:31.680,0:01:33.800 This was a period of... 0:01:34.320,0:01:36.680 like, the first real experience[br]with the language 0:01:36.680,0:01:39.340 and a bunch of people[br]from different backgrounds... 0:01:40.740,0:01:42.500 giving their opinions on the language 0:01:42.500,0:01:45.090 and trying to push it[br]into their favorite direction. 0:01:45.360,0:01:48.420 And, we had a lot of like[br]experiments and dead ends 0:01:48.420,0:01:51.300 and overhauls and false starts and... 0:01:53.300,0:01:54.840 just lots and lots of churn. 0:01:54.840,0:01:58.610 We would sometimes come up with[br]a breaking change in the morning, 0:01:58.610,0:02:00.700 and then have a patch ready[br]in the afternoon 0:02:00.700,0:02:03.420 and then convince someone[br]to merge it later on and then... 0:02:03.860,0:02:07.550 Because there was only one codebase,[br]we'd just fix everything right away 0:02:07.550,0:02:09.490 and people could continue working. 0:02:09.490,0:02:11.130 There was some... 0:02:11.860,0:02:15.390 some trickiness with actually[br]getting like a compiler... 0:02:15.390,0:02:18.480 that compiles the current code[br]after you'd make a breaking change, 0:02:18.480,0:02:21.280 so you'd first change the compiler,[br]then upload a snapshot, 0:02:21.280,0:02:23.400 then change the code,[br]and then you could - 0:02:23.400,0:02:25.860 everyone could - proceed[br]with the new snapshot. 0:02:26.020,0:02:28.940 And then, of course,[br]a year and a half ago - I think - 0:02:30.580,0:02:33.220 the team cut version 1.0, 0:02:33.400,0:02:37.220 and then the process changed entirely,[br]so now it's like... 0:02:37.500,0:02:39.320 everything stays backwards compatible. 0:02:39.320,0:02:44.310 It's impressive how seriously they have[br]been taking backward compatibility. 0:02:46.460,0:02:49.930 Experiments move like RFCs[br]move very slowly 0:02:49.930,0:02:51.650 and there has to be a wide consensus 0:02:51.650,0:02:54.570 and it has to fit[br]within the current codebase. 0:02:55.480,0:02:58.380 So, that's a whole different stage again. 0:02:59.140,0:03:02.830 I'm going to be mostly talking about[br]the period where I was part of the team. 0:03:02.830,0:03:05.900 which was 2011-2012, 0:03:06.360,0:03:08.380 and which was probably[br]the wildest period 0:03:08.380,0:03:13.060 in terms of features cut,[br]features changed - stuff like that. 0:03:14.560,0:03:16.780 So, it may seem a bit ridiculous 0:03:16.780,0:03:19.820 that we put so much time into[br]really complicated features 0:03:19.820,0:03:22.300 just to end up dropping them again. 0:03:23.360,0:03:27.500 But, I think it's kind of an essential[br]part of getting a complex design 0:03:27.500,0:03:30.020 like a programming language right that, 0:03:30.490,0:03:33.420 unless you're a super-genius,[br]you won't really see in advance 0:03:33.420,0:03:35.760 what the implications[br]and the interactions between 0:03:35.760,0:03:40.460 the various parts of the system are[br]and you have to try it and see 0:03:40.620,0:03:43.760 how well you can make it work[br]and how well it fits into the system, 0:03:43.760,0:03:46.370 and sometimes you later have to just... 0:03:46.720,0:03:48.300 abandon it again. 0:03:48.980,0:03:53.240 I think that's part of[br]a healthy design process for like... 0:03:53.620,0:03:56.840 mere mortals who need to[br]actually see how something works 0:03:56.840,0:03:59.380 before they can evaluate it. 0:03:59.900,0:04:03.800 I'm structuring this talk about...[br]around a number of visions 0:04:03.800,0:04:07.020 that were part of the language[br]and then dropped again. 0:04:07.480,0:04:12.040 And, I'll try to explain why I think that,[br]in every case, 0:04:12.040,0:04:14.520 it was a real good decision to drop them. 0:04:15.420,0:04:17.899 But, it's still interesting to see 0:04:18.640,0:04:22.200 what the original visions were[br]and what we did end up with. 0:04:22.820,0:04:27.740 So, these are typestate,[br]a structural type system and 0:04:28.740,0:04:32.860 lightweight processes,[br]and finally, garbage collection. 0:04:33.960,0:04:36.120 Let's start with typestate. 0:04:38.140,0:04:40.830 Typestate is... it was actually 0:04:40.830,0:04:44.560 an important point in initial[br]announcements of the language. 0:04:44.560,0:04:46.720 and people were very excited about it. 0:04:47.920,0:04:50.780 What typestate does is basically[br]allows you to... 0:04:51.220,0:04:55.600 allows the compiler to know more[br]about value than just its type. 0:04:55.600,0:04:57.100 So, an example would be... 0:04:57.560,0:04:59.320 This is something of type [sockets], 0:04:59.320,0:05:04.080 but we also happen to know that it's open[br]or this is something of type array, 0:05:04.080,0:05:07.280 but we happen to know... or say vector[br]in the current terminology, 0:05:07.280,0:05:09.640 but we happen to know that[br]it's not empty. 0:05:09.820,0:05:13.480 Something like that allowing you to add 0:05:13.640,0:05:17.380 more safety to your program -[br]more static guarantees. 0:05:19.380,0:05:23.320 So, when you're programming,[br]you usually have some mental model 0:05:23.320,0:05:26.900 of why the thing you are doing[br]right now is... 0:05:27.240,0:05:29.980 is valid - is not going to crash - 0:05:30.030,0:05:32.130 like if you're not just making[br]random changes 0:05:32.130,0:05:35.480 and seeing if the [tests pass],[br]you will have 0:05:35.660,0:05:39.440 some mental model of your... program. 0:05:39.600,0:05:42.040 And, to a certain degree,[br]depending on the language, 0:05:42.040,0:05:44.840 you can tell the compiler[br]about this model 0:05:44.840,0:05:46.400 and the compiler can then check 0:05:46.400,0:05:49.040 whether you are applying[br]your model consistently. 0:05:49.040,0:05:53.160 So, the simple case is just types -[br]that you're actually passing the type 0:05:53.160,0:05:55.260 that you think you are passing somewhere. 0:05:55.260,0:05:58.700 And, if you don't, then,[br]instead of finding out at runtime, 0:05:58.700,0:06:01.500 you find out at compile time -[br]and this is nice. 0:06:01.500,0:06:03.040 There's a kind of... 0:06:04.420,0:06:07.960 this computer does not have the fonts[br]that my computer had but... 0:06:07.960,0:06:10.300 Imagine arrowheads on both sides. 0:06:10.300,0:06:13.540 There's a kind of spectrum[br]on which languages fall 0:06:14.140,0:06:16.040 in terms of how much 0:06:16.820,0:06:19.140 you can actually communicate[br]to the compiler, 0:06:19.140,0:06:21.070 so down on one side,[br]there's Javascript, 0:06:21.070,0:06:23.920 and it's like... syntactically correct. 0:06:23.920,0:06:25.700 Okay, let's go ahead - let's run it. 0:06:25.700,0:06:28.720 And then, there's like,[br]on the... way on other side, 0:06:28.720,0:06:32.400 there's languages like Coq,[br]which require you to actually construct 0:06:32.400,0:06:37.200 a formal proof that your program does[br]what it's supposed to do - 0:06:37.200,0:06:40.400 that it does so in bounded time,[br]in bounded space, 0:06:42.420,0:06:45.940 which means you're making[br]a lot less mistakes. 0:06:45.940,0:06:48.250 But, on the other hand,[br]it's like a major... 0:06:49.060,0:06:53.280 a major project to write a small program[br]in such a language. 0:06:54.240,0:06:57.840 And, there is a reason that[br]not everyone is writing their web servers 0:06:57.840,0:06:59.360 in Coq or whatever. 0:06:59.360,0:07:01.180 And, Rust kind of falls in the middle. 0:07:01.180,0:07:03.980 It does have quite a bit[br]of static guarantees 0:07:03.980,0:07:05.840 and it helps quite a lot. 0:07:06.240,0:07:11.240 But, it still aims to be ergonomic,[br]like easy to program in, 0:07:12.140,0:07:14.400 where you don't have to 0:07:14.660,0:07:17.740 spend too much time[br]working on these things. 0:07:20.500,0:07:25.240 And I... one way to see the history[br]of programming languages is kind of... 0:07:26.400,0:07:28.780 one aspect of it at least is 0:07:28.780,0:07:32.960 that we've been finding better[br]and better vocabulary to... 0:07:33.140,0:07:37.560 to describe these things we know about[br]our program to the compiler 0:07:39.360,0:07:41.100 in a way that's actually convenient. 0:07:41.100,0:07:43.360 So, if you have a really terrible[br]type system, 0:07:43.360,0:07:46.060 that's often worse than[br]no type system at all. 0:07:46.060,0:07:49.120 If I have to choose to write something[br]in Java or Javascript, 0:07:49.120,0:07:51.780 [I'll just take Javascript,[br]thank you very much.] 0:07:52.130,0:07:53.760 But, we're getting better at this, 0:07:53.760,0:07:55.975 and Rust is making[br]a big contribution here, 0:07:55.975,0:07:58.600 and like bringing a real,[br]modern type system 0:07:58.600,0:08:00.420 to the systems programming space. 0:08:00.420,0:08:03.120 And the ownership model is - I think - 0:08:03.120,0:08:04.550 just really, really good. 0:08:04.550,0:08:07.770 I unfortunately wasn't on the team[br]anymore when this was introduced, 0:08:07.770,0:08:09.680 so I can't take any credit for it, but.. 0:08:09.680,0:08:12.180 I think that this is[br]the most exciting part of Rust 0:08:12.180,0:08:14.420 and it's exactly this kind of thing[br]where you... 0:08:14.420,0:08:17.960 where the compiler knows[br]what you're trying to do and 0:08:17.960,0:08:21.320 tells you when you're[br]violating your model. 0:08:22.800,0:08:24.760 So, back to typestate. 0:08:26.200,0:08:28.360 It looked somewhat like this. 0:08:29.640,0:08:31.880 You could define predicates, 0:08:31.880,0:08:35.140 which is this "pure function not empty"[br]at the top 0:08:38.679,0:08:42.059 The extra information that[br]the compiler had about your values 0:08:42.059,0:08:44.820 came in the form of this predicate hold 0:08:44.820,0:08:48.570 But, these were actually just predicates[br]written in normal Rust code 0:08:48.570,0:08:50.050 that were supposed to be pure. 0:08:50.050,0:08:53.780 There was a concept of an fx system[br]at that point - which is also gone now. 0:08:53.780,0:08:59.320 But, they just took a value and said:[br]"Okay, I hold or I don't hold." 0:09:00.500,0:09:04.920 And, then you could define[br]for your functions, 0:09:04.920,0:09:07.020 preconditions and postconditions. 0:09:07.020,0:09:08.980 So, you could say, for example, 0:09:08.980,0:09:13.440 this function "last" here[br]demands that its first argument 0:09:13.440,0:09:16.520 has the "not empty" predicate[br]holding on it, 0:09:17.100,0:09:20.620 because you can't take the last element[br]from an empty array... 0:09:24.440,0:09:27.240 Then, before you could pass[br]such a value to such a function 0:09:27.240,0:09:31.560 you'd have to convince the compiler[br]that this predicate held at this point. 0:09:31.760,0:09:34.140 For some things,[br]this worked relatively well: 0:09:34.140,0:09:36.900 the compiler was very clever[br]in propagating its information 0:09:36.900,0:09:39.480 through the control flow graph[br]and like taking it 0:09:39.480,0:09:42.360 from the post conditions[br]of the functions you called. 0:09:43.520,0:09:45.380 But here, you have, for example, 0:09:45.380,0:09:48.920 I create an array[br]and then I want to pass it to last. 0:09:49.120,0:09:52.180 But, it's not okay: I first have to... 0:09:53.040,0:09:54.820 check that it's not empty. 0:09:54.820,0:09:58.940 And, this is actually...a sink check[br]would insert a runtime test, 0:09:58.940,0:10:03.020 call to the predicate,[br]and then panic if it failed. 0:10:05.900,0:10:08.160 But actually, I mean,[br]this array isn't empty: 0:10:08.160,0:10:09.640 this is very easy to prove. 0:10:09.640,0:10:12.700 But, because the compiler[br]only saw these predicates as 0:10:12.700,0:10:15.000 like opaque pieces of code, 0:10:15.180,0:10:17.240 it couldn't actually reason about them - 0:10:17.240,0:10:19.460 it could only take what you told it. 0:10:19.460,0:10:22.000 Like, if you checked[br]there was variance of check, 0:10:22.280,0:10:26.140 one of them which just was[br]like an unsafe form of 0:10:26.140,0:10:27.570 "just believe me: this holds." 0:10:27.570,0:10:29.740 So, that might also have been[br]appropriate here 0:10:29.740,0:10:32.440 because I'm really sure that[br]this array is not empty. 0:10:34.200,0:10:37.320 And then, there was a one version that 0:10:38.360,0:10:41.540 ensured that the compiler already[br]statically knew at this point 0:10:41.540,0:10:44.840 that something else... that it was[br]kind of an assertion of "okay... 0:10:46.140,0:10:50.160 "I must notice at this point,[br]but don't insert a runtime check. 0:10:50.720,0:10:54.380 "I want to have a static error[br]if it's not provable." 0:10:56.300,0:10:59.660 But, in my experience,[br]the effect of this system 0:10:59.660,0:11:03.640 was mostly that you would be[br]littering your code with check statements 0:11:03.640,0:11:06.380 and they would also panic at runtime. 0:11:06.380,0:11:09.740 So, the amount of static guarantees[br]wasn't very great 0:11:09.740,0:11:13.520 because often... usually the compiler[br]couldn't really help a lot with 0:11:13.520,0:11:17.260 like reasoning about when[br]they actually held and when they didn't. 0:11:18.180,0:11:20.970 It was in the compiler[br]for a long time still 0:11:20.970,0:11:24.400 but eventually it was dropped because[br]it was just not pulling its weight. 0:11:24.400,0:11:27.660 So, in terms of experiments[br]in good expressive ways 0:11:27.660,0:11:30.960 to express these kind of things,[br]I think this was a failed experiment. 0:11:30.960,0:11:34.000 It existed in some research[br]languages before 0:11:34.000,0:11:38.000 but it's never really made it into[br]a big mainstream type language - 0:11:38.380,0:11:40.100 for good reason, I think. 0:11:41.580,0:11:43.440 So, so much for that. 0:11:43.820,0:11:46.780 Next topic is "structural typing." 0:11:47.060,0:11:51.720 So, in typing systems,[br]you have two concepts 0:11:52.520,0:11:54.520 where structural typing is... 0:11:55.380,0:11:58.380 say you have a function type,[br]which has a few arguments types, 0:11:58.380,0:12:00.480 and a return type,[br]and you want to compare it 0:12:00.480,0:12:02.040 to another function type. 0:12:02.040,0:12:05.740 So, you're just going to look at[br]the fields in the function: 0:12:05.740,0:12:07.960 does it have the same amount[br]of arguments, 0:12:08.240,0:12:12.660 are its arguments of compatible types,[br]is it return type of compatible type? 0:12:13.240,0:12:15.000 And, that's structural. 0:12:15.140,0:12:17.260 On the other hand,[br]there is a nominal typing, 0:12:17.260,0:12:19.630 where you just say:[br]"Where is this type declared? 0:12:19.630,0:12:22.440 "What's the name of this type?" -[br]and it has to be the same. 0:12:22.440,0:12:25.060 So, Rust's structs currently work[br]this way, 0:12:25.400,0:12:28.440 as do enums -[br]two types are only compatible 0:12:28.440,0:12:31.200 if they are actual instances of[br]the thing that was declared 0:12:31.200,0:12:33.800 in the same point in the code. 0:12:36.360,0:12:38.760 Initially, structs were structural types. 0:12:38.760,0:12:43.420 So, this curly braces thing there[br]is syntax for a struct type, 0:12:43.420,0:12:47.540 with two fields - x and y -[br]of type "float." 0:12:48.220,0:12:52.120 And, the type declaration[br]just defines an alias for the type. 0:12:53.180,0:12:58.120 This is just like a name for the type [br]record with two float fields. 0:12:59.960,0:13:03.560 So, if I define a function,[br]which takes an argument of this point, 0:13:03.740,0:13:08.460 I can call it with just a record[br]constructed on the fly 0:13:08.460,0:13:10.940 without any record name involved. 0:13:11.920,0:13:13.820 Records themselves don't have a name: 0:13:13.820,0:13:16.440 they just have a structure[br]in this system, and... 0:13:17.320,0:13:19.940 it's kind of nice and lightweight[br]and minimal, 0:13:19.940,0:13:23.560 and often you don't even bother[br]to give your record a name 0:13:23.560,0:13:25.480 if you only use it a few times. 0:13:26.560,0:13:29.440 So, where you would now[br]probably use it triple, 0:13:29.440,0:13:33.500 you could use a record[br]with nice descriptive field names. 0:13:33.500,0:13:36.540 I kind of liked it for programming with... 0:13:38.940,0:13:42.100 But, I'll come back later[br]to why this part was removed. 0:13:43.300,0:13:46.200 Another aspect of this was object types, 0:13:46.540,0:13:49.080 whereas structure types[br]were only compatible 0:13:49.080,0:13:52.660 if they had actually[br]the exact same fields and... 0:13:53.800,0:13:54.880 in the same order. 0:13:54.880,0:13:58.140 They weren't reordered[br]because of C compatibility 0:13:58.140,0:14:02.000 and they had to be the exact same[br]to be able to compile it efficiently 0:14:02.000,0:14:05.260 because then all code[br]that interacted with such a record 0:14:05.260,0:14:07.440 knew how it was laid out in memory. 0:14:07.640,0:14:12.440 Objects were a more dynamic feature,[br]and here... 0:14:14.320,0:14:19.020 any object type that has a subset[br]of the fields - 0:14:19.020,0:14:21.780 fields are always methods -[br]so they're always functions. 0:14:23.300,0:14:26.020 This object type has, its compatible,[br]so I could, 0:14:26.660,0:14:29.800 if I define the type - a collection of T -[br]with these two - 0:14:29.800,0:14:33.200 it's probably not a very great abstraction[br]but just bear with me - 0:14:33.200,0:14:36.540 with a length and[br]an item accessor method, 0:14:37.140,0:14:41.440 you could take any object that has[br]I don't know what kind of... 0:14:42.360,0:14:44.280 methods with also these two, 0:14:44.280,0:14:46.840 and you could treat it[br]as a collection of T. 0:14:48.160,0:14:52.240 So, these were both the types[br]of the concrete objects 0:14:52.240,0:14:55.980 and also serve the role of interfaces,[br]which is kind of nice in terms of 0:14:55.980,0:14:59.700 how many concepts you need[br]to do object-oriented programming. 0:14:59.990,0:15:02.180 And, you could also even use it[br]as a kind of... 0:15:03.780,0:15:07.700 checked duck typing,[br]where you define your function 0:15:07.700,0:15:09.970 and you just say:[br]"I'm only going to call length 0:15:09.970,0:15:11.820 "on the thing that I'm getting," 0:15:12.140,0:15:15.180 and then, anything that had a length[br]method could be passed in - 0:15:15.180,0:15:18.460 you don't even need to formally define[br]an interface name or anything, 0:15:18.460,0:15:21.660 it's just all like structural by name. 0:15:24.460,0:15:26.300 So... 0:15:27.840,0:15:29.300 One implication of this was 0:15:29.300,0:15:31.920 that because code that used them[br]didn't know their size, 0:15:31.920,0:15:33.680 they always had to be heap allocated. 0:15:33.680,0:15:36.360 I think they even always[br]had to be garbage collected. 0:15:37.600,0:15:42.560 Any calls to them will be going[br]through a dispatch table - a vtable. 0:15:43.200,0:15:46.400 So, they're so much more heavyweight... 0:15:47.620,0:15:49.520 compared to the rest of the language. 0:15:50.000,0:15:52.440 We were finding that, in the compiler, 0:15:52.440,0:15:54.460 we were kind of shying away from them 0:15:54.460,0:15:57.860 and that we absolutely needed[br]polymorphism because 0:15:58.820,0:16:02.180 they were more heavyweight[br]than necessary in many situations. 0:16:05.740,0:16:07.540 Then, at some point... 0:16:09.240,0:16:10.650 Well, there was also 0:16:10.650,0:16:13.040 a lot of machinery involved[br]in actually doing this, 0:16:13.040,0:16:17.480 like upcasting to a type with less methods 0:16:17.480,0:16:20.120 because then you needed to[br]allocate a new vtable, 0:16:20.120,0:16:23.120 preferably statically,[br]which forwarded to the alt object 0:16:23.120,0:16:24.640 and created a wrapper. 0:16:26.080,0:16:29.080 It was conceptually simple[br]but not terribly simple to implement. 0:16:29.080,0:16:31.460 And then, at some point... 0:16:32.480,0:16:35.000 we got more [Haskell?] people[br]on the team 0:16:35.000,0:16:37.000 and we all started agitating for 0:16:37.480,0:16:40.250 a typed class kind of implementation 0:16:40.250,0:16:42.860 interface thing that we ended up with now. 0:16:44.400,0:16:47.220 And, because no one really[br]liked these objects very much, 0:16:47.220,0:16:49.800 we migrated to that. 0:16:50.220,0:16:54.520 And, I think they just fit[br]with the language much better. 0:16:54.520,0:16:57.500 They don't require you[br]to put something on the heap. 0:16:57.500,0:17:03.520 They don't require indirect calls unless[br]you actually are using polymorphism. 0:17:04.660,0:17:06.660 So, I think that's a win. 0:17:08.200,0:17:15.180 But, now that we had implementations,[br]which effect a specific type, 0:17:16.680,0:17:18.820 structural records[br]also became problematic, 0:17:18.820,0:17:22.010 because if you're using a record that[br]happens to have the same shape 0:17:22.010,0:17:24.240 in two completely independent contexts 0:17:24.620,0:17:29.130 and they both define say[br]a two string implementation of it, 0:17:29.130,0:17:30.190 then these will clash, 0:17:30.190,0:17:33.340 even though the actual usages[br]have nothing to do with each other, 0:17:33.340,0:17:36.079 they will both be trying to[br]implement the same interface - 0:17:36.079,0:17:37.260 the same trait on it. 0:17:38.200,0:17:40.160 That doesn't work, so... 0:17:40.340,0:17:43.340 Well, no one cared that much[br]about structural records either, 0:17:43.340,0:17:47.100 so they became nominal[br]for this reason at that point. 0:17:47.420,0:17:48.330 And now, of course, 0:17:48.330,0:17:50.640 people say functions[br]are still structural - 0:17:50.640,0:17:53.280 they don't make much sense[br]in any other way. 0:17:53.280,0:17:58.000 But, the like heavy emphasis[br]on structural typing was abandoned... 0:17:59.220,0:18:01.220 again for good reasons.