WEBVTT

00:00.000 --> 00:12.360
All right, so we have Apple. It's going to give us a really good talk about abusing

00:12.360 --> 00:18.000
reburring for fun, profits, and a safe point garbage collector. Take it away.

00:18.000 --> 00:25.520
Thank you. Hello, everyone. I'm stupid excited to give this talk, so I might be talking

00:25.520 --> 00:32.520
a bit fast, and maybe a bit loud, but I'm Apple. I work at Bulmit Automation over in Finland.

00:32.520 --> 00:38.600
I'm a software architect there building a browser-based automation UI platform, so this

00:38.600 --> 00:45.600
stuff that I work in TypeScript actually can bring plants down in cost millions of dollars

00:45.600 --> 00:51.320
or euros a day if it comes down to that's fun. I'm a quiet singer, I'm an open source

00:51.400 --> 00:57.800
enthusiast and I love Albatross, especially. I'm a data oriented design zealot, and I'm also

00:57.800 --> 01:03.840
developing with some other friends, Nova JavaScript engine in Rust, so I'm a Rust developer

01:03.840 --> 01:10.480
by night, and this is basically going to be about Nova, but not quite so clearly.

01:10.480 --> 01:17.760
Okay, quick refresh on lifetimes and borrowing in Rust, so we have lifetimes and we have

01:17.840 --> 01:22.200
references, and those are two different things. You must remember that. It's important.

01:22.200 --> 01:29.200
Lifetimes, it's when you can access something, when something can be used, and references

01:29.200 --> 01:35.520
is how you access the stuff. With lifetimes we have three types. We have the static lifetime,

01:35.520 --> 01:41.200
works for the whole program, doesn't matter when you use it, just don't mutate it

01:41.200 --> 01:47.080
from multiple threads at the same time. Owned lifetimes, that's like a back or a box, starts

01:47.080 --> 01:54.080
when you create it, and when it ends when it's moved out, somebody else gets that and they get

01:54.080 --> 02:00.280
their own own lifetimes at that point, and then we have generics, lifetimes generics, the

02:00.280 --> 02:08.480
tick A that you take in your function, that is valid for the entirety of the function, for the whole

02:08.480 --> 02:15.040
function called. Some of this might be like not quite correct, but it's good enough at

02:15.040 --> 02:24.080
least. Shared borrows work, or they observe A T for the duration of tick A, and nobody is

02:24.080 --> 02:29.880
allowed to mutate that T while it's being observed. Let's not talk about internal mutation,

02:29.880 --> 02:36.440
that's not relevant today, and then we have exclusive borrows, which basically say I am mutating

02:36.440 --> 02:46.160
T for the duration of tick A, and nobody is allowed to observe that stuff. What is reborrowing

02:46.160 --> 02:53.240
then? Reborrowing is the stuff you do all the time. Whenever you use a reference, you make

02:53.240 --> 03:01.440
a reborrow. Always when you pass a reference to another function as a parameter, it gets

03:01.440 --> 03:09.640
reborrowed for the shorter duration, for the duration of that function called. If that borrow

03:09.640 --> 03:16.640
you give escapes that function somehow, then the compiler will, I assume, anyway, start thinking

03:16.640 --> 03:23.320
how this escapes. Is your longer lifetime, the lifetime that you actually have, is that long

03:23.320 --> 03:31.240
enough to actually encompass that escape? When you reborrow a shared borrow becomes the shared

03:31.240 --> 03:40.400
borrow, when we borrowed. But an exclusive borrow can be reborrowed as shared or exclusive.

03:40.400 --> 03:51.760
And if you reborrow as shared, then as long as the escape doesn't keep used or stay used

03:51.760 --> 04:01.160
for too long, you can then use the exclusive lifetime again afterwards. If it does escape,

04:01.160 --> 04:11.640
then things happen. And you can kind of write a reborrowed signature. We don't have this

04:11.640 --> 04:19.160
in rust currently. We might have it in the future. But a reborrow would be you take the

04:19.160 --> 04:27.440
shorter temporary reference to your reference. Here the T is now your reference, your

04:27.440 --> 04:34.560
longer reference. And you produce a new T, which has this shorter lifetime. That's basically

04:34.560 --> 04:43.040
what reborrowing is. You just don't usually see it until today. Okay. Our challenge today

04:43.040 --> 04:47.240
is to build a safe, exact tracing, safe point garbage collector with unrooted values using

04:47.240 --> 04:53.880
lifetime. A bit of a mouthful, so let's go from the top. A garbage collector is a system

04:53.960 --> 05:01.880
of automatically releasing memory when it's not already used. We could even say that the

05:01.880 --> 05:10.800
borrow checker itself is actually a garbage collector. It's just a compile time one. Reference

05:10.800 --> 05:15.920
counting is another type of garbage collector. But in this case, we are dealing with a tracing

05:16.000 --> 05:21.000
garbage collector, which basically works by following references, whatever reference is, through

05:21.000 --> 05:24.000
items and seeing what items have I traced. And then, at the end, deciding, okay, those ones that I have not seen,

05:37.360 --> 05:44.360
those are unused. They are unreachable. So by definition, then they must be unused. Nobody can

05:44.680 --> 05:51.120
find them anymore anyway. And they can be released. Okay. An exact garbage collector is

05:51.120 --> 06:01.480
then one where the tracing of these items starts from static places. And they follow statically

06:01.480 --> 06:11.960
defined references. The opposite or the kind of other kind of garbage collector is a conservative

06:12.040 --> 06:18.840
garbage collector. In a conservative garbage collector, you start by scanning the stack. You

06:18.840 --> 06:23.640
just go through the stack and try to find things that look like references. You probably have some kind

06:23.640 --> 06:28.680
of an idea what a reference looks like in this case. And then you follow those and you maybe you even

06:28.680 --> 06:35.720
scan stuff there to see if these look like references. And then you keep going with that. An exact

06:35.800 --> 06:42.840
garbage collector knows exactly where it should start. What it should scan and based on what it sees

06:42.840 --> 06:50.040
after scanning, it knows where to continue. And then finally, safe point. A safe point garbage

06:50.040 --> 06:56.680
collector is one where the garbage collection only happens at certain times. And in on our case,

06:57.800 --> 07:03.080
because we have an exact safe point collector, and we have unrooted values,

07:03.720 --> 07:11.320
at when we're coming to a garbage collector's safe point, we must have all of our values

07:11.320 --> 07:17.320
in statically known places. Basically this means that they must be on the heap. We have some kind

07:17.320 --> 07:22.680
of heap where we're allocating stuff. This is a JavaScript engine after all. And JavaScript is like

07:22.680 --> 07:30.360
massively heap allocating. So when garbage collection might be about to occur,

07:31.240 --> 07:38.840
all values that you had on the stack must be put on the heap. And then after the safe point has

07:38.840 --> 07:47.960
passed, now you can read them back from the heap. Does that make sense? So values on the stack

07:47.960 --> 07:56.840
must be rooted before safe point. The way you usually would do this in like C++ or C or something

07:56.840 --> 08:03.960
is by not doing this. You would never ever write this because holy price you don't want to be manually

08:03.960 --> 08:10.600
tracking when a safe point's happening and have you rooted all of your things. So you would just

08:10.600 --> 08:18.040
always have rooted things on the stack. Or you have a conservative garbage collector which will just

08:18.040 --> 08:24.200
scan the stack. Rust gives us lifetimes and we can use that for a great effect. So let's look at

08:24.280 --> 08:31.400
code and now let's hope that my looking at code actually works because I had a system. Oh yes,

08:31.400 --> 08:38.760
good, good. I'm not actually seeing this on my screen. So I will be looking at the code with you.

08:39.880 --> 08:46.200
So we're looking at my code editor here directly. Let's open this. That's not open that.

08:47.080 --> 08:56.520
Step one. We have our arena. That's like a heap allocated stuff. It holds some stuff there and

08:56.520 --> 09:03.240
we get an exclusive reference to it. And then we have some kind of API. This get function which

09:03.240 --> 09:07.400
gives us a value from the heap. It doesn't matter what kind of value it gives us. It doesn't matter

09:08.200 --> 09:15.080
what it is. But in this case, it's giving us references. It's actual honest guard references to

09:15.160 --> 09:24.200
apparently U32s in the heap. And what we want is basically in this case, these are references.

09:24.200 --> 09:29.400
So it's not going to work. But what we would want is that when we get the first item from the heap

09:30.040 --> 09:35.720
that we can print it and then we can get the second value from the heap and print that as well.

09:38.360 --> 09:43.640
And we would still be able to print this first value as well because we're just kind of

09:44.360 --> 09:50.200
we're just making changes to the heap. But the heap guarantees that these items are not

09:51.640 --> 09:57.160
they're not going to be invalidated before garbage collection happens before we enter a safe point.

09:59.560 --> 10:03.640
And the get function in this case. It takes an actual mute self.

10:05.400 --> 10:11.880
So what we end up with is that we can indeed run this code

10:13.880 --> 10:15.880
or go wrong.

10:19.080 --> 10:27.400
Yes, it works. But when I try to print the first value after I've added the second value,

10:27.400 --> 10:35.160
it no longer runs because obviously I'm mutating the arena and the reference no longer works.

10:35.160 --> 10:39.880
Because these are actual references and this is correct that it doesn't work.

10:40.040 --> 10:44.040
So this is not really much of the garbage collector yet.

10:46.520 --> 10:51.160
So let's take the route taken by the talk a couple of,

10:53.320 --> 10:58.680
couple of minutes prior or 10 minutes prior, maybe an hour or two prior.

10:59.400 --> 11:06.920
Let's just take use sizes. So now our first function here just returns a use size.

11:07.000 --> 11:11.800
It still mutates the arena, but it just returns a use size. And we get a second value and now,

11:13.080 --> 11:22.600
with this we can print the first and the second value before we call garbage collection.

11:23.160 --> 11:28.120
But unfortunately, we can also use them freely after we call the garbage collection.

11:29.480 --> 11:35.400
And we see a panic because it's like over indexing.

11:35.480 --> 11:37.640
That's not really what we want.

11:40.600 --> 11:44.360
Okay. Let's try this again.

11:48.040 --> 11:53.880
Yep. So this is a bit error prone and this is like how you would do it in like C++ or something.

11:53.880 --> 11:58.520
You basically just get like values that have no safety to that.

11:59.800 --> 12:04.200
So we don't really want to do this. Where did my mouse go?

12:05.480 --> 12:16.360
Okay. But maybe we can add a lifetime to the value that we take back.

12:17.880 --> 12:27.080
So I have created this arena ref type here and now my get function returns an arena ref.

12:28.040 --> 12:34.520
We can you can see it it just takes mute self and returns an arena ref.

12:34.520 --> 12:39.400
And the arena ref has is just a use size with a phantom data reference inside of it.

12:41.000 --> 12:44.120
And now things will magically work, right?

12:47.800 --> 12:53.240
Let's see. I can't even compile it anymore. I can't even print the first value anymore.

12:53.880 --> 13:03.160
Why? Because the get function it takes a mute exclusive access to the arena.

13:03.160 --> 13:07.160
So it's saying I will be mutating the arena.

13:07.160 --> 13:10.840
Then it returns the first value arena ref.

13:10.840 --> 13:20.840
And that it makes the lifetime of the mute exclusive reference escape to the first value.

13:21.800 --> 13:25.400
Which then means that I think I'm getting echo.

13:27.240 --> 13:35.080
Which then means that the first is saying as long as I live as long as I exist, I keep mutating the arena.

13:36.920 --> 13:43.080
And then on the next line I'm trying to say hey, could you observe the arena while you mutate the arena?

13:43.640 --> 13:46.040
And Rust says no, you can't do that.

13:46.680 --> 13:48.680
No, let's not.

13:48.680 --> 13:54.520
So this is like this seems, this isn't just raw.

13:56.760 --> 14:02.600
In software development there's a rule that anything can be solved with extra in direction.

14:03.560 --> 14:07.960
And I think anything can be always solved with adding an extra lifetime as well.

14:08.440 --> 14:18.120
So maybe if we add like this token thing, this token reference here, it's just an empty struct.

14:19.320 --> 14:24.040
Zero choice type. But I take a reference to it. And now my get function

14:25.240 --> 14:29.960
takes the token by reference. But it takes a shared reference.

14:30.520 --> 14:43.800
The function here takes an actual exclusive reference and then below my GC function here takes an exclusive reference again.

14:45.400 --> 14:49.000
And now now things like this seems like it might work.

14:50.680 --> 14:56.280
On the first line, I've kind of helped fully written these out on the side lines if you have been reading them.

14:56.600 --> 15:03.160
But on the first line, when we get the first item, it's lifetime kind of, it's own lifetime.

15:04.360 --> 15:13.080
And this tick one here starts. And then when we take the second, it's lifetime tick two starts.

15:13.640 --> 15:20.040
And these are both bound kind of to the tick A, but only in a shared way.

15:20.040 --> 15:26.920
So it's fine that they are in existence at the same time. So we can indeed run this code

15:29.480 --> 15:35.000
and get the values printed out at the same time before we do garbage collection.

15:35.000 --> 15:43.240
But then if I try to run garbage collection and then print the values, now no longer works.

15:43.240 --> 15:50.440
Because now Rust is saying, wait, you've you've excluded exclusive access to the token.

15:51.400 --> 15:57.800
And then you're trying to say that I can still but like keep references to it. Not going to happen.

15:57.800 --> 16:07.640
No. So this, this looks like it will work. Now we're kind of, this is basically us getting the garbage

16:07.640 --> 16:13.000
or getting Rust to understand how our garbage collector works, garbage collector works.

16:14.200 --> 16:17.240
Values are valid until garbage collection might happen.

16:21.000 --> 16:29.560
Okay. What happens if we pass these arena refs as parameters? Because I mean, when you have like an

16:29.560 --> 16:34.600
actual function, it would be nice to have actual parameters. Imagine a JavaScript engine where you

16:34.680 --> 16:42.120
can't pass any parameters, kind of useless. Maybe it's not too useless compared to the usual

16:42.120 --> 16:55.640
JavaScript, but I mean, anyway. I don't know how I'm for time. But so what we have here now is that

16:55.640 --> 17:03.160
I've added this Act 2 function, which takes the first and second values. And then if we look at

17:03.240 --> 17:12.360
the Act 2 function, it just does what we did previously in line. It prints the two values and then

17:12.360 --> 17:20.360
does G.C. And I've written out the lifetimes here because obviously like this should be bound together.

17:21.720 --> 17:28.600
Arena refs, they should be observing the token. So like this is how you do it, right? Makes sense.

17:28.680 --> 17:46.360
So if we run this, it's working before I didn't even call Act 2. Now let's do Act 2. And it does

17:46.360 --> 17:57.000
not compile. Why? Because I am passing the arena refs, which say I am observing the token,

17:57.960 --> 18:04.120
to this function. And then I pass the token exclusive. The Act 2 function takes it as exclusive.

18:04.120 --> 18:11.160
So I'm also passing in the mutation. So basically I'm saying, hey, could you observe this

18:11.160 --> 18:16.280
token while you mutate it? And Rust does no, no, I can't do that. I'm sorry, Dan or Dave.

18:17.880 --> 18:24.760
No worries, no worries. I know how to fix this. We can do some UB.

18:25.560 --> 18:37.560
Yes, it's time for the best function in Rust standard mem transmit. Yes, yes, this helps us.

18:37.560 --> 18:48.040
So I take the arena ref and I just say, don't worry about it. It's fine, it's fine. Now Rust

18:48.120 --> 19:00.200
allows me to actually call this function. Yes, Act 2 is on. And now, obviously, if I

19:01.480 --> 19:15.160
uncomment these prints here, then it will no longer, oh. Why isn't Rust help? Why? Why aren't they

19:15.160 --> 19:20.920
binding to that together? That should be an error, right? Rust should be saying you can't

19:20.920 --> 19:27.320
observe the token while you also have it shared. I've added these tick A's here into the

19:27.320 --> 19:35.320
beginning. This should be the same thing, right? Well, it doesn't work that way. As I said,

19:35.320 --> 19:40.600
somewhere in the beginning, the tick A is valid for the duration of the call. For the

19:40.600 --> 19:45.080
entire duration of the call, there is no, these are not bound together somehow. These are not

19:45.080 --> 19:51.960
the same lifetime or not the same borough. They are the same lifetime, the same duration.

19:52.840 --> 19:58.680
But from Rust's perspective, these are no longer connected at all in any way. Well, no worries.

19:58.680 --> 20:03.320
I know how to fix this. Can you guess? Maybe you can.

20:11.320 --> 20:25.800
Yes, it's the second best function in Rust standard, same transmute. So, I take the arena

20:25.800 --> 20:32.040
riff. I don't, at this point, I don't give a damn what kind of a lifetime it has inside of it.

20:32.040 --> 20:39.160
And I take a shared act borough of the token. And I say, hey, arena riff, you're now observing

20:39.800 --> 20:47.400
this token. If you want to be like actually, if you actually want to think about this,

20:47.400 --> 20:55.640
like what is actually happening, I'm just creating a new item, new arena riff. So, I'm creating

20:55.640 --> 21:06.200
a new own lifetime, which then carries on or observes the token. And now Rust will indeed tell

21:06.280 --> 21:15.640
me that you can't do this. I'm sorry Dave, that's forbidden. So, okay, we can, we can pass parameters,

21:16.360 --> 21:27.000
requires a bit of lifetime plans meets, but it's fine, fine. Okay, okay. Well, what about returning

21:27.000 --> 21:31.160
values? Sometimes it would be nice to get some results as well.

21:31.640 --> 21:43.480
I added to the act two function here, just a return of a new arena riff. This is just one more

21:43.480 --> 21:55.000
arena get, get called there at the end. And this works wonderfully. It prints, I had the unbind

21:55.640 --> 22:01.160
stuff in the function already. So, it prints the first and second and now the third value here.

22:02.760 --> 22:13.080
And then I can, I can still add like or get more stuff from the arena. And if I try to,

22:14.520 --> 22:21.800
when I wear it, my cursor go again. If I try to print the third value after GC, it obviously doesn't

22:21.800 --> 22:32.600
work, but it does work before the arena get function there. And then if I print the third value

22:33.640 --> 22:42.360
here in the middle, then it obviously still works. Oh, no, no, it doesn't. Why doesn't work?

22:43.960 --> 22:49.240
Well, it doesn't work. If we ask Rust, why doesn't it work? Mutable borrow occurs here.

22:51.960 --> 22:56.840
And immutable borrow occurs here. Kind of borrow token is immutable because it is also borrowed

22:56.840 --> 23:05.960
is mutable. Mutable borrow later used here. So, the third value here, when I take this, when I call

23:05.960 --> 23:15.000
act two, it escapes the exclusive reference to the token or its lifetime. So, basically the third

23:15.640 --> 23:20.920
value there is saying, I am constantly mutating the token. You are not allowed to observe it.

23:23.640 --> 23:29.320
Damn. No worries. I know how to fix this. Yes, yes, yes.

23:29.320 --> 23:48.280
There we go, much better. Yes, now it works perfectly. It's always fine, always fine.

23:48.840 --> 23:57.480
So, we are actually, have I been going at this like power speed? I don't know.

23:59.880 --> 24:06.040
There is one thing that I don't particularly like with this, and it's the reference. The token

24:06.040 --> 24:13.480
reference is a real actual pointer, which is being passed from function to function, but it's never

24:13.560 --> 24:23.000
the reference. It has no actual real use. The thing we want is the lifetime, only the lifetime,

24:23.000 --> 24:34.200
not the reference. So, what we can do is we just turn the token to a phantom data entirely.

24:34.680 --> 24:42.360
Struct, which contains a phantom data of a borough. In this case, it's an exclusive borough

24:42.360 --> 24:49.000
there inside the phantom data, but the exclusivity there doesn't actually matter. It's just kind of foreshow.

24:52.360 --> 25:00.120
And then this token, exclusive token, struct has a couple of methods. It has a method shared,

25:01.080 --> 25:08.520
which takes a temporary borough of self, shared borough of self, and returns a shared token,

25:09.080 --> 25:19.320
which contains a phantom data of a borough reference. So, essentially that shared token there escapes

25:20.600 --> 25:24.840
the lifetime of that temporary shared self borough.

25:24.920 --> 25:34.360
And then there's another function called a reborrow, which takes an exclusive self reference,

25:34.360 --> 25:41.640
and returns a new exclusive token, which now escapes the lifetime of that exclusive self borough.

25:43.880 --> 25:50.760
And with these two things, the same code that we had before works perfectly well.

25:50.840 --> 26:05.080
No more tricks, no more jokes, no more horrible standard meme transmutes. They're still there, of course,

26:05.080 --> 26:13.960
but I'm just not showing them anymore. This works perfectly. And we no longer have that unnecessary

26:14.040 --> 26:22.760
reference going out. The zero size type that we're passing in, except for one Windows calling convention,

26:22.760 --> 26:28.120
it gets completely optimized out by the compiler. It does not exist at runtime,

26:28.120 --> 26:32.600
which is perfect because this is not runtime stuff, this is build time stuff.

26:32.680 --> 26:45.640
But so far, we haven't really actually gotten to the point of like, how is this garbage collection?

26:45.640 --> 26:50.920
What is, how does this connect with garbage collection? Of course, we kind of see that this is

26:50.920 --> 26:58.520
keeping us from holding the values beyond points of garbage collection, but what if we do need to

26:58.520 --> 27:05.480
use the values after garbage collection? For that, we need a new type and a new lifetime,

27:05.480 --> 27:12.200
because all things can be sold with another lifetime. So I've added here to the token,

27:12.200 --> 27:17.320
it still has the first lifetime, which this, I would be calling it the garbage collector lifetime,

27:17.320 --> 27:21.080
and then there's a second lifetime, which I would be calling the scope lifetime.

27:22.360 --> 27:27.720
And the scope lifetime is never kind of used exclusively, it is always shared, so it is basically

27:27.880 --> 27:36.040
just valid for the whole duration of this call, and then I add this scope function.

27:37.320 --> 27:46.600
And the scope function is a takes the arena ref by value, takes a shared token,

27:48.280 --> 27:57.080
so this says I will not call any garbage collection at this point, and then it returns a scope

27:57.160 --> 28:02.520
arena ref, and the scope arena ref is now bound to the shared tokens second lifetime.

28:03.640 --> 28:12.040
IE, it says that this scope arena ref is valid for the duration of this entire function call,

28:12.600 --> 28:20.760
and maybe even beyond it, that if you return it, it can escape. And now with this scope arena ref type,

28:21.720 --> 28:28.200
it has a get function which can be used to get the arena ref back from inside

28:29.160 --> 28:35.400
the scope arena ref. And what this scope function actually does is that it takes the reference

28:36.040 --> 28:43.880
that you have, puts it on the heap, and then one garbage collection might have run, you read it back

28:43.960 --> 28:51.480
from the heap. Now we have scoped, we have rooted, this is scoped rooting or rooting

28:51.480 --> 28:56.520
or values when garbage collection might, might be happening or is about to happen.

28:56.760 --> 29:16.040
Yeah, that's most of that. Actually this is like a good point, good place to ask some

29:16.120 --> 29:28.760
questions if you have a question about the code. Why? That's kind of a good question.

29:30.280 --> 29:37.720
The why is basically based on my experience from writing a JavaScript engine,

29:38.760 --> 29:43.560
most values don't need rooting, and every time you root something,

29:43.560 --> 29:54.200
you waste a bit of time. So if you can work with unrooted values, most of the time,

29:55.080 --> 30:02.440
you probably should, and a lot of engines will like fire force for instance, has they have an

30:02.440 --> 30:09.080
internal link tool which basically is doing the same kind of thing. It's like a whole program

30:09.080 --> 30:15.400
optimizer which tries to make sure that no function that doesn't expect garbage collection

30:17.000 --> 30:22.840
can ever call into functions that might end up calling garbage collection. So they kind of do this

30:24.200 --> 30:33.720
same thing, but just in a very manual way. But this is pretty ugly, I must admit.

30:33.960 --> 30:40.760
There are things that can be made nicer. So the rebaro and shared calls, those are the

30:41.320 --> 30:47.080
one of the main things that could be made nicer. There's an RFC for auto rebaro trades.

30:48.280 --> 30:56.920
If you can help me push that along, I'm willing to push that along, but I probably need help with that.

30:57.000 --> 31:07.320
I don't know, rust person or rust current, whatever. The unbind calls for method parameters,

31:07.960 --> 31:19.960
that could be done with not too much new stuff, I think. Interprocedural rebaroing is basically

31:19.960 --> 31:27.880
what it would be. I actually have an extra example where this comes up in normal rust code.

31:29.000 --> 31:39.160
And then the returning values having to do unbind and bind again, that needs a whole new feature

31:39.160 --> 31:44.920
which probably requires quite a bit of stuff. The problem there, why we can't, why can't that

31:44.920 --> 31:53.320
happen automatically? Why does it need to keep exclusivity? It's basically because cells exist.

31:54.920 --> 32:03.560
Internal mutation is the root of all evil apparently. Even if you are given an exclusive

32:03.560 --> 32:09.640
reference and then you know you don't use it in a kind of particularly bad way. It's actually

32:09.640 --> 32:14.760
possible that the exclusive reference you were given is created in a particularly bad way,

32:15.320 --> 32:22.920
which if it was automatically kind of released when you return a reference based on that,

32:23.960 --> 32:33.400
you could run into you be with that. This is probably the point where people start standing up

32:33.400 --> 32:41.720
and no one can actually ask questions. So I will just say that on GitHub, the code is there available.

32:43.000 --> 32:48.120
The Nova JavaScript engine which we are developing, we have our own website there where you can find

32:48.760 --> 32:55.800
more or less interesting blog posts about how and why the engine is built and then our code

32:55.880 --> 33:04.200
GitHub is also there. If you have questions, throw them up. If you don't, I don't know,

33:04.200 --> 33:11.400
come forward to me afterwards. I would just say that could we stay seated until the questions are

33:11.400 --> 33:22.360
over. We couldn't do any questions because of it. And apparently I kept timing this at home and

33:22.360 --> 33:26.440
I could not keep it in 40 minutes and now I'm like 33 minutes, one.

33:28.840 --> 33:32.920
Simply in a way to roast and not any other language.

33:34.200 --> 33:40.440
Okay, that's an excellent question and I'm very happy that you asked it because I was actually

33:40.440 --> 33:45.000
telling this to my co-workers just this week. They loved hearing about it. I was telling you.

33:45.080 --> 33:51.080
Roast is basically the only language that I know of where this can be done.

33:52.440 --> 34:00.200
So the unrooted values on the stack with an exact garbage collector is such an insane thing

34:00.200 --> 34:08.040
without the borough checker. With these unbind, bind, that song and dance, it can be made work.

34:08.280 --> 34:16.360
It's manual. It's still somewhat error prone, which is also partially why the engine does not

34:16.360 --> 34:26.360
actually use the lifetime guarantees to do unchecked indexing. All indexes are checked because

34:26.360 --> 34:36.760
you probably make mistakes and we do want to crash with that. But yeah, like the borough checker

34:36.840 --> 34:46.280
is an essential tool here to enable this kind of system. And in a more kind of performance

34:46.920 --> 34:55.720
focused view, the conservative garbage collector's are generally apparently faster.

34:56.760 --> 35:02.440
And I believe one of the reasons is that exact garbage collectors have to jump through a lot of

35:02.440 --> 35:10.360
hoops to get that. I either need to do a lot of routing and so on, where conservative garbage

35:10.360 --> 35:15.560
collector's don't. And I'm kind of hoping that this might actually turn the time.

35:21.560 --> 35:26.520
There's no more questions. Let's thank Apple. One more question, sorry.

35:27.480 --> 35:40.680
So does this work on Nightly Rust? This is entirely stable Rust. We do not have nightly

35:40.680 --> 35:46.680
features turned on at all. And that's basically by design. I don't really want to go to the

35:46.840 --> 35:48.840
nightly train and then never leave.

36:04.120 --> 36:11.960
And, naively, it feels like the unbind and bind sung-and-dance is ever prone and might undermine

36:11.960 --> 36:17.480
most of the benefits you're getting from the borough checker. Do you want to speak to you experience

36:17.480 --> 36:32.520
some practice on that? So, like, yes, it does indeed, it does indeed undermine the kind of the

36:32.600 --> 36:44.840
benefit. Or, it's a necessary evil. I wish we didn't need to do it, but the borough checker is built

36:44.840 --> 36:51.160
with the idea of working with actual references. In our case, these are not actual references.

36:51.160 --> 36:58.040
The values are actually indexes into vectors. And we never compact those vectors. The vector

36:58.120 --> 37:04.680
is never change values. They can only add values before garbage collection doesn't happen.

37:05.640 --> 37:13.880
So that's why we can say that these are valid values. If we make a mistake, the worst things that

37:13.880 --> 37:22.440
can happen is that either we overindex and crash, or your value, your object changes to some other

37:23.320 --> 37:27.560
object. It's still the same type of object every time. You can't change to another type.

37:27.560 --> 37:34.840
So, there is no actual, there is no, like, type safety problems here. No type confusion is possible.

37:34.840 --> 37:41.000
So, that in that way, I'm also kind of not too worried about this. And if you really wanted to,

37:41.000 --> 37:46.840
you could actually write the same code without transmutes. You would just take out the internal

37:46.840 --> 37:55.480
index and put in, like, create a new arena ref, which contains that index. The transmute is

37:55.480 --> 38:08.600
just a bit easier for reasons. Okay, if that's it, then thank you. Our pro for this great talk.

