Skip to content

Commit 73c54d9

Browse files
bors[bot]Dirbaio
andauthored
Merge #331
331: spi: enforce all traits have the same Error type. r=ryankurte a=Dirbaio Previously discussed in #323, split off to its own PR now. This PR enforces all SPI trait impls for a given type use the same Error associated type, by moving it to its own ErrorType trait. ## How breaking is this? I believe this is not very breaking in practice (it won't make upgrading existing code to 1.0 harder), because: - HALs already use the same error type for all methods (I haven't seen one that doesn't) - Drivers already often add bounds to enforce the errors are the same. [Example](https://github.com/rust-iot/rust-radio-sx127x/blob/8188c20c89603dbda9592c40a8e3fc5b37769a00/src/lib.rs#L122). Without these bounds, propagating errors up would be more annoying, drivers would need even more generic types `SpiWriteError, SpiReadError, SpiTransferError, SpiTransactionalError`... ## Why is this good? Traits being able to have different Error types is IMO a case of "bad freedom" for the HALs. Today's traits don't stop HALs from implementing them with different error types, but this would cause them to be incompatible with drivers with these bounds. If traits force error types to be the same, the problem is gone. ## What about other traits? I believe this should be also applied to the other traits. I propose discussing this in the context of SPI here, and if the consensus is it's good I'll send PRs doing the same for the other traits. Co-authored-by: Dario Nieuwenhuis <[email protected]>
2 parents 28a2dcf + 37edca7 commit 73c54d9

File tree

4 files changed

+25
-42
lines changed

4 files changed

+25
-42
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212

1313
### Fixed
1414
- Fixed blanket impl of `DelayUs` not covering the `delay_ms` method.
15+
### Changed
16+
- `spi`: traits now enforce all impls on the same struct (eg `Transfer` and `Write`) have the same `Error` type.
1517

1618
## [v1.0.0-alpha.6] - 2021-11-19
1719

src/spi/blocking.rs

+8-36
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
//! Blocking SPI API
22
3-
/// Blocking transfer with separate buffers
4-
pub trait Transfer<W: Copy = u8> {
5-
/// Error type
6-
type Error: crate::spi::Error;
3+
use super::ErrorType;
74

5+
/// Blocking transfer with separate buffers
6+
pub trait Transfer<W = u8>: ErrorType {
87
/// Writes and reads simultaneously. `write` is written to the slave on MOSI and
98
/// words received on MISO are stored in `read`.
109
///
@@ -17,37 +16,27 @@ pub trait Transfer<W: Copy = u8> {
1716
}
1817

1918
impl<T: Transfer<W>, W: Copy> Transfer<W> for &mut T {
20-
type Error = T::Error;
21-
2219
fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
2320
T::transfer(self, read, write)
2421
}
2522
}
2623

2724
/// Blocking transfer with single buffer (in-place)
28-
pub trait TransferInplace<W: Copy = u8> {
29-
/// Error type
30-
type Error: crate::spi::Error;
31-
25+
pub trait TransferInplace<W: Copy = u8>: ErrorType {
3226
/// Writes and reads simultaneously. The contents of `words` are
3327
/// written to the slave, and the received words are stored into the same
3428
/// `words` buffer, overwriting it.
3529
fn transfer_inplace(&mut self, words: &mut [W]) -> Result<(), Self::Error>;
3630
}
3731

3832
impl<T: TransferInplace<W>, W: Copy> TransferInplace<W> for &mut T {
39-
type Error = T::Error;
40-
4133
fn transfer_inplace(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
4234
T::transfer_inplace(self, words)
4335
}
4436
}
4537

4638
/// Blocking read
47-
pub trait Read<W: Copy = u8> {
48-
/// Error type
49-
type Error: crate::spi::Error;
50-
39+
pub trait Read<W: Copy = u8>: ErrorType {
5140
/// Reads `words` from the slave.
5241
///
5342
/// The word value sent on MOSI during reading is implementation-defined,
@@ -56,44 +45,32 @@ pub trait Read<W: Copy = u8> {
5645
}
5746

5847
impl<T: Read<W>, W: Copy> Read<W> for &mut T {
59-
type Error = T::Error;
60-
6148
fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
6249
T::read(self, words)
6350
}
6451
}
6552

6653
/// Blocking write
67-
pub trait Write<W: Copy = u8> {
68-
/// Error type
69-
type Error: crate::spi::Error;
70-
54+
pub trait Write<W: Copy = u8>: ErrorType {
7155
/// Writes `words` to the slave, ignoring all the incoming words
7256
fn write(&mut self, words: &[W]) -> Result<(), Self::Error>;
7357
}
7458

7559
impl<T: Write<W>, W: Copy> Write<W> for &mut T {
76-
type Error = T::Error;
77-
7860
fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
7961
T::write(self, words)
8062
}
8163
}
8264

8365
/// Blocking write (iterator version)
84-
pub trait WriteIter<W: Copy = u8> {
85-
/// Error type
86-
type Error: crate::spi::Error;
87-
66+
pub trait WriteIter<W: Copy = u8>: ErrorType {
8867
/// Writes `words` to the slave, ignoring all the incoming words
8968
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
9069
where
9170
WI: IntoIterator<Item = W>;
9271
}
9372

9473
impl<T: WriteIter<W>, W: Copy> WriteIter<W> for &mut T {
95-
type Error = T::Error;
96-
9774
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
9875
where
9976
WI: IntoIterator<Item = W>,
@@ -119,17 +96,12 @@ pub enum Operation<'a, W: 'static + Copy = u8> {
11996

12097
/// Transactional trait allows multiple actions to be executed
12198
/// as part of a single SPI transaction
122-
pub trait Transactional<W: 'static + Copy = u8> {
123-
/// Associated error type
124-
type Error: crate::spi::Error;
125-
99+
pub trait Transactional<W: 'static + Copy = u8>: ErrorType {
126100
/// Execute the provided transactions
127101
fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>;
128102
}
129103

130104
impl<T: Transactional<W>, W: 'static + Copy> Transactional<W> for &mut T {
131-
type Error = T::Error;
132-
133105
fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error> {
134106
T::exec(self, operations)
135107
}

src/spi/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,15 @@ impl core::fmt::Display for ErrorKind {
113113
}
114114
}
115115
}
116+
117+
/// SPI error type trait
118+
///
119+
/// This just defines the error type, to be used by the other SPI traits.
120+
pub trait ErrorType {
121+
/// Error type
122+
type Error: Error;
123+
}
124+
125+
impl<T: ErrorType> ErrorType for &mut T {
126+
type Error = T::Error;
127+
}

src/spi/nb.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Serial Peripheral Interface
22
3+
use super::ErrorType;
4+
35
/// Full duplex (master mode)
46
///
57
/// # Notes
@@ -16,10 +18,7 @@
1618
///
1719
/// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different
1820
/// `Word` types to allow operation in both modes.
19-
pub trait FullDuplex<Word: Copy = u8> {
20-
/// An enumeration of SPI errors
21-
type Error: crate::spi::Error;
22-
21+
pub trait FullDuplex<Word: Copy = u8>: ErrorType {
2322
/// Reads the word stored in the shift register
2423
///
2524
/// **NOTE** A word must be sent to the slave before attempting to call this
@@ -31,8 +30,6 @@ pub trait FullDuplex<Word: Copy = u8> {
3130
}
3231

3332
impl<T: FullDuplex<Word>, Word: Copy> FullDuplex<Word> for &mut T {
34-
type Error = T::Error;
35-
3633
fn read(&mut self) -> nb::Result<Word, Self::Error> {
3734
T::read(self)
3835
}

0 commit comments

Comments
 (0)