Restore Session::finish call in socket ref

Using move semantics allows an Option to keep track of the
initialization state while satisfying the borrow checker. This replaces
the functionality that the 'consumed' flag had before. It also retains
the smaller object size since the Option of a reference can use the
representation of the null pointer, which is an invalid reference, as a
niche for the None variant.

Closes: #301
Approved by: whitequark
This commit is contained in:
Andreas Molzer 2019-07-23 13:11:25 +02:00 committed by Homu
parent 10e251b0aa
commit 8e1fa540c3
1 changed files with 19 additions and 18 deletions

View File

@ -32,7 +32,12 @@ impl<'a> Session for TcpSocket<'a> {}
/// ///
/// Allows the network stack to efficiently determine if the socket state was changed in any way. /// Allows the network stack to efficiently determine if the socket state was changed in any way.
pub struct Ref<'a, T: Session + 'a> { pub struct Ref<'a, T: Session + 'a> {
socket: &'a mut T /// Reference to the socket.
///
/// This is almost always `Some` except when dropped in `into_inner` which removes the socket
/// reference. This properly tracks the initialization state without any additional bytes as
/// the `None` variant occupies the `0` pattern which is invalid for the reference.
socket: Option<&'a mut T>,
} }
impl<'a, T: Session + 'a> Ref<'a, T> { impl<'a, T: Session + 'a> Ref<'a, T> {
@ -42,7 +47,7 @@ impl<'a, T: Session + 'a> Ref<'a, T> {
/// ///
/// [into_inner]: #method.into_inner /// [into_inner]: #method.into_inner
pub fn new(socket: &'a mut T) -> Self { pub fn new(socket: &'a mut T) -> Self {
Ref { socket } Ref { socket: Some(socket) }
} }
/// Unwrap a smart pointer to a socket. /// Unwrap a smart pointer to a socket.
@ -55,8 +60,8 @@ impl<'a, T: Session + 'a> Ref<'a, T> {
/// be sure to call [new] afterwards. /// be sure to call [new] afterwards.
/// ///
/// [new]: #method.new /// [new]: #method.new
pub fn into_inner(ref_: Self) -> &'a mut T { pub fn into_inner(mut ref_: Self) -> &'a mut T {
ref_.socket ref_.socket.take().unwrap()
} }
} }
@ -64,25 +69,21 @@ impl<'a, T: Session> Deref for Ref<'a, T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.socket // Deref is only used while the socket is still in place (into inner has not been called).
self.socket.as_ref().unwrap()
} }
} }
impl<'a, T: Session> DerefMut for Ref<'a, T> { impl<'a, T: Session> DerefMut for Ref<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.socket self.socket.as_mut().unwrap()
} }
} }
// FIXME: `Session::finish` currently does not do anything, but if it did, this would be a problem, impl<'a, T: Session> Drop for Ref<'a, T> {
// because `SocketRef::into_inner` would have to use unsafe code, and there's currently no unsafe fn drop(&mut self) {
// code in smoltcp at all (other than the `phy` module). The reason it would need unsafe code is if let Some(socket) = self.socket.take() {
// that it is currently an error to destructure a value that implements Drop (or move out its Session::finish(socket);
// fields in any other way), so it'd have to be transmuted away. This is a deficiency in Rust: }
// it is always safe to ignore the Drop impl during destructuring. }
// }
// impl<'a, T: Session> Drop for Ref<'a, T> {
// fn drop(&mut self) {
// Session::finish(self.socket);
// }
// }