5 ms·
This is precisely the problem addressed by typestates and session types. Each state has a unique type, and in that state, one can only call the appropriate func
by sriram_malhar 8y ago
This is precisely the problem addressed by typestates and session types. Each state has a unique type, and in that state, one can only call the appropriate functions.
Interestingly, Rust used to have typestates in its very early incarnation.
See: https://pcwalton.github.io/2012/12/26/typestate-is-dead.html https://pcwalton.github.io/2012/12/26/typestate-is-dead.html
- Sharlin 8y agoRust's affine typing solves precisely this problem as well. Your state change methods consume the previous state variable, making it a compile-time error to access it afterwards. Given code like in the article and assuming connect_unauthenticated takes self by value: let i1 = socks5_socket(); let i2 = i1.connect_unauthenticated(proxy_addr); let i3 = i1.connect_tcp(addr); Rust will complain: error[E0382]: use of moved value: `i1` | 2 | let i2 = i1.connect_unauthenticated(proxy_addr); | - value moved here 3 | let i3 = i1.connect_tcp(addr); | ^ value used here after move | = note: move occurs because `i1` has type `socks5_socket`, which does not implement the `Copy` trait
- goldenkey 8y agoThat is really marvelous. How is this kind of thing implemented under the hood? That new wizkid language Zig by Andrew Kelly - I wonder if it has anything similar?
- a1369209993 8y agoYou can actually do the type-checking part of this even in C, it's just uglier: struct sock5_blank i1 = socks5_socket(); struct sock5_authed i2 = connect_unauthenticated(i1,proxy_addr); struct sock5_tcp i3 = connect_tcp(i1,addr); /* error: expected struct sock5_authed, got struct sock5_blank */ Rust adds the constraint that any (non-copyable,non-dropable) variable has to be passed to a function (or operator, or return statement) exactly once: Noncopyable a = ...; Copyable b = ...; f(a,b); //=> f(a,copy(&b)) // a is no longer in scope after this g(b); // this is fine, we took a reference to b and made a copy without changing the original h(a); // error: a is no longer a live variable
- rumcajz 8y agoThe problem with the C solution is that you'll get hanging pointers. Try using i1 after the code above executes and you'll get a runtime error.
- capitalsigma 8y agoYou can use C++ unique_ptr and turn on Clang warnings about "use after move", if you want that level of protection.
- a1369209993 8y agoEr, yes; that's what I said (or rather, hanging various things including but not limited to pointers). C only supported the typechecking part of this because it doesn't have any facilities for compile-time linearity checking. Linearity[0] and dependency[1] checking is what makes Rust a improvement over C, rather than yet another "let's reinvent C++, but slightly less awful" project. 0: "error: no implementation for copy〈T〉","error: no implementation for drop〈T〉"[2] 1: "error: cannot move/destroy x (of type T); y (of type T&'x) outlives it"
- tiuPapa 8y agoI think this is a feature of Rust's ownership model.
- sriram_malhar 8y agoYes, between traits and alias control, you have everything required to solve OP's problem safely and efficiently. The only remaining aspect, I feel, is aesthetic, as the errors show; they show aliasing/ownership errors, but not indicative of operations on a single state machine.