# Programming Style

Congratulations, you've found a secret level!

This is a super work-in-progress page which collects various rules-of-thumb I use. The primary goal
so far is to collect the rules for myself, that's why I don't link to this page from anywhere yet.

## General

### Naming

Prefer full names except for extremely common cases (`ctx` for context), or equal-length pairs
(`next/prev`). Use consistent names. Naming variables after types (`let thing: Thing`) is a way
to achieve global consistency with little coordination.

 Build  a vocabulary of standard names and re-use it:

: `ctx`

  "context" of an operation. Typically holds something mutable. Read-only
  context is named `params`.

: `params`

  A bag of named arguments. Unlike `config`, might hold not only pod types.

: `config`

  Generally user-specified POD parameters.

: `sink`

  "output" of an internal iterator, typically `sink: &mut FnMut(T)` or `sink: &mut Vec<T>`.

: `lhs`, `rhs`

   operands of a binary operator.

: `fuel`

   Recursion and infinite loop guards

: `result`

   A "return" variable.

: `line_index`, `line_number`

   Index is unambiguously zero-based. By convention, number is one-based.


Equisized pairs;

- add/sub, mul/div
- lhs/rhs
- s/e
- next/prev
- source/target
- src/dst
- index/count
- insert/remove
- beg/end
- fresh/stale
- prefix/suffix
- receive/consume
- internal/external
- opening/closing

### Explicit Data Tables

Remove code duplication by extracting commonalities into tabular data

```
// GOOD
const cases = ["foo", "bar", "baz"];
for case in cases {
    if x == case {

    }
}

// BAD
if x == "foo" {

} else if x == "bar" {

} else if x == "baz" {

}
```

### Bulk IO

Avoid opening file descriptors in favor of bulk operations. To write data to a file, you need to
follow a lifecycle: open file descriptor, issue write syscalls, close file descriptor. Lifecycle
handling requires complicated type-system machinery and is better avoided. Usually, standard library
provides something like `std::fs::read_to_string` which encapsulates lifecycle management.


## Rust

### No Self Types

Write types out explicitly, avoid `Self` alias if possible:

```rust
// Good
pub struct Diagnostic {
    pub code: DiagnosticCode,
    pub text: String,
}

impl Diagnostic {
    pub fn new(code: DiagnosticCode, text: String) -> Diagnostic {
        Diagnostic { code, text }
    }
}

// Bad
impl Diagnostic {
    pub fn new(code: DiagnosticCode, text: String) -> Self {
        Self { code, text }
    }
}
```

*Rationale:* reducing cognitive load, optimizing for the reader.
Resolving `Self` is a small mental effort, it can be avoided.

### Prefer new Over default

Use `new` over `default` to construct instances.

*Rationale:* new is too ingrained.

### Blank Line Between Declarations

Leave blank line between top-level declarations:

```rust
// Good
impl Foo {
    pub fn foo() {
    }

    pub fn bar() {
    }
}

// Bad
impl Foo {
    pub fn foo() {
    }
    pub fn bar() {
    }
}
```

*Rationale:* consistency.
Omitting blank line leads to somewhat terser code, but is very hard to do consistently.

### Derive Order

Use the following order of derives:

```rust
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
```

*Rationale:* consistency.
Debug comes last because it is the most often added item.o
