You 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