Binary Search Trees in JavaScript

Before the advent of cellphones and the internet, we used these things called phone books. Most of you probably know what a phone book is, but just in case: a phone book is a directory where…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Closures

It’s called ‘lambda’ in most languages, isn’t it? Python example: foo = lambda x: x+2.

I always jump to conclusions… Next phrase from the book:

Yep, that sounds more like a closure.

I read that chapter, page after page, but they intentionally kept the main feature of closures away from a reader. I lost my patience and created my first closure:

It prints 3, 4 (as expected) and is simple to understand. Closure keep values frozen inside itself.

Let’s try to play with a scope.

Nah!

A shrewd compiler, I’d say. A small modification makes things work:

To internalize the closure idea I say that:

I’m continue to follow the chapter in the book. Closure looks like Python lambda in one specific way: they have no type annotations, like in any dynamically typed language. But Rust isn’t dynamically typed.

Type inference is our friend here. Let’s test that closures aren’t dynamically typed:

The result:

Not a dynamically typed, indeed. But we have generics…

Nah. Nothing have worked.

The same answer promised something interesting in the future:

But it is, indeed, does not compile in my current Rust version (1.24.1+dfsg1–1).

So, there are no generics for closures.

In the previous session I’ve struggled with AsRef trait. Now the book presented me with a simple T: Fn(u32) -> u32 trait, which explains a lot. The trais’s syntax is so good, that it allows to demand a type of an argument which has implementation with a specific signature.

Further reading pointed me to a new generic usage in for impl:

As far as I could understand, it says that implementation for structure Cacher is limited to the same type T as it was used for Cacher. Very explicit.

I found another twist to closures. A move keyword is in the Rust.

But if I use no move for closure, what would happens?

Yep, it’s a “mutation of immutable borrow” error:

The result of the next experiment was completely unexpected to me.

It compiles, it works (output is ‘5, 4’), but why Rust allows me to move value (move is an ownership movement, right?) and then allow to assign value to the ‘moved’ variable?

It looks like we transfer ownership of the value, not the variable. As soon as we moved the value we can assign to the variable a new value. But this is strange. The ground is very shaky for me here…

Can we do the same trick without closures?

Let’s start from this example:

It works and compiles as expected.

If we add borrowing it’s no longer working.

Error:

Both are expected and I understand why one works and another doesn’t.

But together they are breaking my implicit assumption, that an ownership transfer is more restrictive then borrowing. It’s not true.

When we transfer ownership, we release variable of it’s value. We can store another value in it. But if we borrow value, we are keeping bound between the variable and it’s value. We can’t throw away the value from variable (it’s borrowed, and it’s need to be the same for all it’s lifetime duration).

Insofar I find this discovery the most important for this session. It’s a big insight on the true nature of ownership/borrow model. Borrowing does not ease things, it gives features in exchange for restrictions.

I return back to the closure’s move. It’s now sensible: we use move to move ownership into a closure, and without move we do borrow instead. Do we? Let’s me to check this.

I show you one (wrong) example I tried to play with. It’s not the thing I want to check, but I’ll save it just to keep chronology of my attempts.

If I replace closure with let z = move |x| x; nothing will change. Both examples are compilable, both examples produce the same output. But there is no difference between those examples because in both cases there are no actual closures involved. The value is calculated within the scope for y=3, so I can just write l=y, and nothing will change.

Here is a corrected example. It does work only if I add ‘move’ to the closure.

Error:

It confirms my model:

I thought I’ll do a whole chapter today, but no. I learned about (lambda) closures in Rust, and, more importantly, I corrected my interpretation of ownership/borrow model. The next time I’ll finish this chapter (iterators) and I will return back to the IO chapter, to continue to study of ‘reading a file’ scenario down to a libc/syscall level.

Add a comment

Related posts:

Is the Health Insurance system broken?

Purchasing health insurance if self-employed seems cost-prohibitive when it comes to deductibles, monthly cost, and coverage in general. Seems like it is only good for catastrophic situations…