-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use marker types for Port read/write access #248
Conversation
Looks like this change causes some minor type inference breakage in the CI tests. I feel like in this specific case the data type should be specified anyway. Maybe this breakage is okay and I should just update the test? |
Thanks a lot for the pull request! To avoid the inference issues we could make If we want to avoid breakage completely, we could rename |
I think I like the first option to have separate constructors. I'll add that and make sure the documentation is clear about which one to use. |
Actually, to stay on the theme of this change of having the Port type be generic over access, it would be nice to still have some constructor which is generic over access. If |
Hmm actually looking at it, having to do |
The way I have it now I think looks the cleanest, but it's definitely a breaking change as what was |
src/instructions/port.rs
Outdated
phantom: PhantomData<T>, | ||
/// An access marker type indicating that a port is only allowed to read values. | ||
#[derive(Debug)] | ||
pub struct ReadOnlyAccess { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we write these Access structs as pub struct ReadOnlyAccess(());
? It's a little shorter.
src/instructions/port.rs
Outdated
} | ||
|
||
impl<T> Port<T> { | ||
/// A read-write I/O port. | ||
pub type PortReadWrite<T> = Port<T, ReadWriteAccess>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a big fan of this breaking change. I would prefer PortGeneric<T,A>
which is specialized as: Port<T>
, etc...
Given that users will commonly use Port
and rarely use PortGeneric
, keeping the smaller name makes more sense to me (and it avoids a breaking change).
I think we just need to change the names, the rest of the interface looks great to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @dbeckwith for working on this!
src/instructions/port.rs
Outdated
f.debug_struct("PortGeneric") | ||
.field("port", &self.port) | ||
.finish() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we could include the access type here as well, e.g. as an additional field? What do you think about this, @josephlr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The access level seems like useful info to have in the debug output, I like that idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I played around with ways to add an "access" field to the Debug representation, and it's a little messy: bbb0493
But it produces nice output:
PortGeneric {
port: 80,
size: 1,
access: ReadWrite,
}
An alternative to this messy implementation is to just use const generics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So using const generics doesn't seem too bad, I put together #254 and it looks reasonable.
The debug output looks like:
PortGeneric {
port: 80,
size: 1,
read: true,
write: true,
}
I added a sealed One final question: Do we want to have Personally, I would be fine with just having |
The problem I see with using a single type is the If I could redesign this API today, I would probably choose a similar approach like I did in the |
It only really needs the type annotation for the |
Signed-off-by: Joe Richey <[email protected]>
Signed-off-by: Joe Richey <[email protected]>
You're right that users that already specify a type (e.g. let mut port = Port::new(0xf4);
port.write(exit_code as u32); I'm not sure how common such usage is, but I would like to avoid any breakage (even if it's technically allowed) to such a low level type because it might lead to breakage in other libraries that depend on it. So I think it's better to be careful in this case. Alternatively, we can of course do a breaking release, but I'm not sure if this is worth it. Maybe it's a good idea to merge this with |
I think that's the most reasonable solution:
I agree that |
Sounds like a good plan! |
Changes the
Port
struct to use a type parameter and marker types for restricting read/write access instead of using entirely separate structs, reducing some code duplication and making it easier to be generic over access types.I'm fairly confident this is a compatible change since the
A
parameter onPort
defaults toReadWriteAccess
andPortReadOnly
andPortWriteOnly
still exist, albeit as type aliases now.This conflicts with #247 a bit but it should be easy for me to deal with the conflicts.