←back to thread

517 points bkolobara | 1 comments | | HN request time: 0s | source
Show context
johnfn ◴[] No.45049034[source]
I think Rust is awesome and I agree with that part of the article.

What I disagree with is that it's the fault of Typescript that the href assignment bug is not caught. I don't think that has anything to do with Typescript. The bug is that it's counter-intuitive that setting href defers the location switch until later. You could imagine the same bug in Rust if Rust had a `set_href` function that also deferred the work:

    set_href('/foo');

    if (some_condition) {
        set_href('/bar');
    }
Of course, Rust would never do this, because this is poor library design: it doesn't make sense to take action in a setter, and it doesn't make sense that assigning to href doesn't immediately navigate you to the next page. Of course, Rust would never have such a dumb library design. Perhaps I'm splitting hairs, but that's not Rust vs TypeScript - it's Rust's standard library vs the Web Platform API. To which I would totally agree that Rust would never do something so silly.
replies(7): >>45049104 #>>45049184 #>>45049492 #>>45049625 #>>45049709 #>>45052032 #>>45052981 #
bkolobara ◴[] No.45049492[source]
Thanks! I should have clarified a bit better that example.

The point I was trying to make is that Rust's ownership model would allow you to design an api where calling `window.set_href('/foo')` would take ownership of `window`. So you would not be able to call it twice. This possibility doesn't exist at all in TypeScript, because it doesn't track lifetimes.

Of course, TypeScript can't do anything here either way. Even if it had knowledge of lifetimes, the JavaScript API already existed before and it would not be possible to introduce an ownership model on top of it, because there are just too many global variables and APIs.

I wanted more to demonstrate how Rust's whole set of features neatly fits together and that it would be hard to get the same guarantees with "just types".

replies(1): >>45049634 #
johnfn ◴[] No.45049634[source]
I'm not as familiar with Rust, but isn't there still a gap? For instance, if we modified window.set_href to have move semantics, wouldn't this still work (i.e. not produce an error)?

    let win = window.set_href("/foo")
    win.set_href("/bar")
You might say "why would you ever do that" but my point is that if it's really the lack of move semantics that cause this problem (not the deferred update), then you should never be able to cause an issue if you get the types correct. And if you do have deferred updates, maybe you do want to do something after set_href, like send analytics in a finally() block.

In fact, Typescript does have a way to solve this problem - just make `setHref` return never[1]! Then subsequent calls to `setHref`, or in fact anything else at all, will be an error. If I understand correctly, this is similar to how `!` works in Rust.

So maybe TS is not so bad after all :)

[1]: https://www.typescriptlang.org/play/?ssl=9&ssc=1&pln=9&pc=2#...

replies(2): >>45049967 #>>45050736 #
bkolobara ◴[] No.45049967[source]
Your Rust example would not work, because `window.set_href("/foo")` would not return anything (it would return the unit type "()" aka void). And you can't call `set_href()` again on "()". This is a common pattern in Rust, allow certain functions to only be called once on specific objects.

I really like your TypeScript solution! This actually perfectly solves the issue. I just wish that this was the ONLY way to actually do it, so I would not have experience the issue in the first place.

replies(1): >>45050217 #
1. johnfn ◴[] No.45050217{3}[source]
Thanks for the explanation! That's a very nice pattern.

> I just wish that this was the ONLY way to actually do it

I completely agree.