Better support incremental development through semantic aliases

No one writes production code right out of the gate. We all take shortcuts when we’re getting started. We don’t know what the right solution is going to look like. We probably don’t even know how to properly handle many of the error cases a new library presents us with. Or if we do, maybe we just want to make sure the happy-path works first.

For this reason I believe we should expose several semantic aliases in the standard library and language for expressing that what you’re looking at is not well-thought-out or production-ready.

Aliases for uncertain number types

Choosing the correct number type for the job is an important thing to do, but not necessarily something obvious when you’re just getting started out. If you see foo: i32 or foo: i64, is it clear that the integer type has been properly audited to fit the use-case, or has it just been chosen arbitrarily (possibly based on $COMPANY’s best-practices for defaults)?

I propose introducing type int = i64 and type float = f64 as types for a programmer to express “TODO: figure out appropriate integer type”. This would be accompanied by a stub_number_types lint set to warn by default. i64 and f64 are chosen because implicit widening would allow everything to be silently placed inside a stub type, and because it reduces the chances of running into an overflow while hashing out details. Yes, potentially covering up an overflow bug is a feature.

There is minor precedent for this in Jonathan Blow’s experimental Jai language, where int and float are provided as aliases for exactly this purpose. Blow’s language is specifically designed for incremental, exploratory development and refactoring.

Aliases for assertion-style operations

If you see an unwrap in a codebase, in can be there for two reasons: the author is certain that the operation succeeds, or the author was stubbing out some code and wasn’t ready to think about it. Unfortunately, with unwrap it’s impossible to tell.

I propose introducing a todo method as an alias for unwrap. This would be accompanied by a stub_assertions lint set to warn by default.

Conventions for unhandled patterns

If you see an _ in a pattern or a let _ =, it’s not clear if a catch-all or suppression is really intended, or if the author hasn’t properly considered all the possibilities.

I propose that _todo be used as a convention for patterns that need more consideration before production. This would be accompanied by a stub_patterns lint set to warn by default.

Other things?

These are the 3 major semantic ambiguities that I’ve encountered while working with Rust, but I haven’t written that much non-toy code outside of std, so I’m willing to bet there are things I’ve missed.

8 Likes
#[prelude]
mod prelude {
    pub type int = i64;
    pub type float = f64;
}
fn main() {
    let x = 12: int;
    println!("{}", (12 as float) / 66.0);
}

@eddyb while I agree that it’s easy for a library to provide this, I think there’s value in having it in the standard distribution; this is about conventions, and conventions are best provided by std. (Also lints are syntax extensions which won’t be stable for 1.0, right?)

2 Likes
  1. IMHO, you should never be publishing code that uses int/uint so I don’t see why convention is really that much of an issue.
  2. What about making it possible to deprecate type aliases?

It might be nice if one could extern crate stub; for development and use these features. I really don’t want to see stuff like this in the standard distro though. Especially with such tempting names like int and float. Maybe unknown_int or unknown_float. It should be inconvenient and ugly, so it doesn’t look like a part of a healthy codebase.

Official publication is a blurry concept at best. Is 0.0.1 really published, or just a proof-of-concept? What about private collaboration? What about just making it easy for you to find where you yourself were stubbing? What about an internal application that will never be "published"?

I fundamentally disagree with punishing people for admitting that they aren't sure. The lints should make it clear that these aren't production constructs as soon as they're used.

2 Likes

It’s not punishing. It’s making hacky code look ugly, and good code look pretty.

It's making lying about what you know more tempting and convenient than being honest. That is to say, just slamming foo: i32 is more convenient than foo: unknown_int. I don't think proof-of-concept code should have to look particularly awkward or scary. I'd rather encourage people to state they're being lazy when they're being lazy.

3 Likes

Isn’t this a perfect use of a BigInt type, that was so highly lauded on the “int semantics” RFC?

1 Like

There are two problems with using a BigInt:

  • The usage of BigInt is completely different (it’s not Copy)
  • There’s no BigInt in std to use

Migrating from BigInt to, say, i32 is probably a much bigger effort than migrating from i64 to i32.

Good point.

To summarize some discussion in IRC:

Aliases for uncertain number types

I propose uki and ukf for the type of “unknown int” and “unknown float”. They’re still short to type (for fast prototyping), but look strange enough that it will raise eyebrows in production code. (Exercise for the reader: why uki instead of iuk?)

Aliases for assertion-style operations

I’d prefer people to explicitly match on Option, and explicitly panic in the None case instead of overusing unwrap. Maybe if they had to type a bit more people would do it less. I think that unwrap should warn.

Two caveats to this point are tests and examples. In tests, unwrap actually is probably what you want (although there’s an argument for making tests return Result<(), TestFailure>). In examples, it saves several lines since you don’t need to wrap the example in a function returning Result<(), ()>, and then explicitly unwrap the result. To address this, unwrap in #[test] code should be allowed without warning.

There’s an argument for allowing it in example code, but I’m of the personal belief that examples should “set a good example”.

Conventions for unhandled patterns

Finally, I think _todo is a good suggestion, but it should just be a convention, instead of a compiler-enforced rule. I know that’s what I’m going to start doing now that I’ve heard of it!

I don’t think unwrap should warn. Overusing unwrap might be bad and a problem but there are enought situations where you know that it is Some and everything else is not a wrong used library but a serious (e.g. because you tested for None a view lines before). Also note that through some of this situations might be transformable into a (nested) match this is not always feasable.

Edit: Expect this point I like the idea especially about the unknown int. And btw. I agree that code generally should use Result (and similar) for all excepted error cases limiting panic to programming errors and a view other special cases if possible. Therefore I think in many cases not having unwrap is nice but the are just to many other cases where it aligns with mentioned ideas even through it panics :wink: .

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.