owned this note
owned this note
Published
Linked with GitHub
# Embedded-HAL Meeting - 2020-03-10
## Final Summary
We agreed on 5 things to do NOW (e.g. aiming for a 1.0 or pre-1.0 release), and two things we MIGHT do in the future, if necessary.
The things that we WILL do NOW/ASAP, that will constitute a BREAKING change on next release:
1. We will KEEP the "monolithic" embedded-hal crate, where all traits are exposed and developed in a single crate.
* This has the impact of closing/rejecting https://github.com/rust-embedded/embedded-hal/pull/169.
2. We will REMOVE versioned traits within the same repo (e.g. `digital::V2`/`digital::V3`). Instead, all breaking changes will be made by making semver changes to the `embedded-hal` crate directly, e.g. going from `0.9.x` to `0.10.0` or `2.x.y` to `3.0.0`.
* This has the impact of closing/rejecting https://github.com/rust-embedded/wg/pull/393
3. For the next major release, we will ONLY be offering *Fallible* traits, e.g. traits that return `Result`s. All trait method names will be prefixed with `try_` for consistency, e.g. `try_send`.
4. We will not be providing backwards compatibility/blanket impls/semver tricks. We instead are aiming for "one simple breaking change", instead of trying to make things as gentle as possible.
5. We will remove the `unproven` feature. In the future, new traits will only be added when they are already "proven stable", outside of the `embedded-hal` crate.
The things that we MAY do in the future, if necessary, but NOT for the initial release:
6. Reintroduce `Infallible` traits on a case-by-case basis, if there is demand. In particular these may be applied to `Digital*` traits, where it is a very common demand.
7. Split up the embedded-hal traits into separate crates, e.g. one for `embedded-hal-uart`, one for `embedded-hal-spi`
### Justification/Reasoning
#### 1 - Monolithic Crate
In general, we saw this choice of balancing two options:
* A monolithic crate that is tightly versioned, offering:
* Preventing N-to-M version mismatches between individual components of embedded-hal traits
* No complicated crate structure, workspaces, submodules, etc.
* Many individual crates (`embedded-hal-serial`, `-spi`, `-digital`), with MAYBE re-exporting all traits from a top level convenience crate (`embedded-hal`). This could be one repo (with or without a workspace), or multiple separate repos. This would offer:
* Ability to make breaking changes on a single-domain item, e.g. `-serial` could have a breaking change while `-spi` wouldn't.
We decided in favor of the former, for the sake of initial simplicity. If this becomes an issue in practice, e.g. many breaking changes in individual traits causing frequent breaking changes to `embedded-hal` as a crate, we may decide to implement [optional item 7](#7-OPTIONAL---Broken-up-repos).
#### 2 - Removing multiple version in one crate
While well intentioned, having multiple version of a trait (particularly with the same Trait/trait method names) seemed to cause problems in practice, particularly in the `digital::(v1|v2|v3)` transitions. Instead, we decided to use Rust's semantic versioning to manage breaking changes.
#### 3 - Only Fallible Traits
This was a point of extensive discussion. In general, it is difficult to consistently handle (either at the target driver, application, or library driver level) a mix of Fallible and Infallible traits. We would likely have problems where:
* A microcontroller driver implements one flavor of a trait, while a library driver requires the other flavor of a trait, OR
* There would be hard to see "unwraps" implicit in the code when providing compatibility
In order to support the general case of "handle errors explicitly", we decided to only offer Fallible traits to start.
If this is TOO painful in practice, in particular with types that almost never are fallible, such as GPIOs using `Digital` traits, we may decide to implement [optional item 6](#6-OPTIONAL---Infallible-traits).
#### 4 - No mitigations for breaking changes
This was also a point of extensive discussion. In practice, all attempts in the past to "mitigate" breaking changes through semver tricks, blanket impls, or other approaches have generally had unforseen negative side effects, either immediately, or when later changes were desired.
Instead, we decided to "keep it simple", which would require many crates to change in a breaking way, but also hopefully in a very simple way. We also discussed tooling to discover and propose changes/PRs across the ecosystem to help mitigate usage issues.
#### 5 - No more `unproven` feature
In the past, managing `unproven` features, and having "sort of breaking" changes have been a struggling point. Also, people tended to adopt `unproven` features quickly, but the features would take a very long time to stabilize.
Instead, we would like to push experimentation OUT of the embedded-hal crate, allowing people to experiment externally, and merge when some kind of feasability had been proven.
We don't plan to specify exhaustively *HOW* to achieve this, but instead provide some examples that could make sense. Examples of how to do this include:
* For standalone traits, e.g. for CAN, you could make a standalone "trait crate". Once it hits community acceptance/"1.0 status", then we can merge it into embedded-hal
* For additions/modifications to existing traits, this could be done by forking embedded-hal, and providing an example impl or two across different targets/drivers to demonstrate feasability. These could be breaking or non-breaking changes
Acceptance of new traits/modules/functions, it would be up to the `embedded-hal` team to discuss and approve changes. These discussions should be "lighter" for non-breaking changes, and breaking changes should be considered carefully.
#### 6 (OPTIONAL) - Infallible traits
See [required item 3](#3---Only-Fallible-Traits).
#### 7 (OPTIONAL) - Broken up repos
See [required item 1](#1---Monolithic-Crate).
## Meeting Notes:
## Topics
- digital:v3 https://github.com/rust-embedded/wg/pull/393
- Make automatic conversion opt-in
- Re-Organizing Repo: https://github.com/rust-embedded/embedded-hal/pull/169
- Criteria to actually accept things into embedded hal? https://github.com/rust-embedded/wg/pull/415
- Remove unproven config gate
James' meta proposal:
- embedded-hal as a top level item
- All independent areas as standalone crates
- No more "proven", we merge things to the meta for approval
problems:
- NxM compatibility problem
Choices:
- Monorepo + Crate
- Monometacrate, individual parts
Splitting crates into pieces:
- Makes workspaces harder to manage
- Submodules can be bad
Two options:
- Workspaces
- Problem publishing
- Totally separate crates
- Have to pull them in, compilation time
Can't get things done, because tedious
```rust
trait Uart {
type Error;
fn try_send(&mut self, u8) -> Result<(), Self::Error>;
// Default, but overridable impl
fn send(&mut self, foo: u8) {
self.try_send(foo).unwrap();
}
}
// in nrf52-hal
impl Uart for nUart {
type Error = Infallible;
fn try_send(&mut self, u8) -> Result<(), Self::Error> {
unimplemented!()
}
}
```
https://github.com/ryankurte/rust-embedded-driver