From 8502541f9ad8367f8516c05ddf021cf4be6cc5b9 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 3 Jan 2025 08:59:25 +0100 Subject: [PATCH 1/5] Replace `Ipv4Network::new_unchecked` with `Ipv4Network::new_checked` --- src/ipv4.rs | 56 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/ipv4.rs b/src/ipv4.rs index e7acdee..4959044 100644 --- a/src/ipv4.rs +++ b/src/ipv4.rs @@ -67,19 +67,17 @@ impl Ipv4Network { /// /// If the prefix is larger than 32 this will return an `IpNetworkError::InvalidPrefix`. pub const fn new(addr: Ipv4Addr, prefix: u8) -> Result { - if prefix > IPV4_BITS { - Err(IpNetworkError::InvalidPrefix) - } else { - Ok(Ipv4Network { addr, prefix }) + match Ipv4Network::new_checked(addr, prefix) { + Some(a) => Ok(a), + None => Err(IpNetworkError::InvalidPrefix), } } - /// Constructs without checking prefix a new `Ipv4Network` from any `Ipv4Addr, - /// and a prefix denoting the network size. + /// Constructs a new `Ipv4Network` from any `Ipv4Addr`, and a prefix denoting the network size. /// - /// # Safety - /// - /// The caller must ensure that the prefix is less than or equal to 32. + /// If the prefix is larger than 32 this will return `None`. This is useful in const contexts, + /// where [`Option::unwrap`] may be called to trigger a compile-time error in case the prefix + /// is an unexpected value. /// /// # Examples /// @@ -87,14 +85,30 @@ impl Ipv4Network { /// use std::net::Ipv4Addr; /// use ipnetwork::Ipv4Network; /// - /// let prefix = 24; - /// let addr = Ipv4Addr::new(192, 168, 1, 1); + /// const PREFIX: u8 = 24; + /// const ADDR: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1); + /// + /// // Okay! + /// const NETWORK: Ipv4Network = Ipv4Network::new_checked(ADDR, PREFIX).unwrap(); + /// ``` + /// + /// ``` + /// use std::net::Ipv4Addr; + /// use ipnetwork::Ipv4Network; + /// + /// // Prefix is greater than 32. + /// const PREFIX: u8 = 32 + 1; + /// const ADDR: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1); /// - /// debug_assert!(prefix <= 32); - /// let network = unsafe { Ipv4Network::new_unchecked(addr, prefix) }; + /// // Unwrap to get a compile-time error! + /// assert!(Ipv4Network::new_checked(ADDR, PREFIX).is_none()); /// ``` - pub const unsafe fn new_unchecked(addr: Ipv4Addr, prefix: u8) -> Ipv4Network { - Ipv4Network { addr, prefix } + pub const fn new_checked(addr: Ipv4Addr, prefix: u8) -> Option { + if prefix > IPV4_BITS { + None + } else { + Some(Ipv4Network { addr, prefix }) + } } /// Constructs a new `Ipv4Network` from a network address and a network mask. @@ -396,11 +410,15 @@ mod test { } #[test] - fn create_unchecked_v4() { - let cidr = unsafe { Ipv4Network::new_unchecked(Ipv4Addr::new(77, 88, 21, 11), 24) }; + fn create_checked_v4() { + let cidr = Ipv4Network::new_checked(Ipv4Addr::new(77, 88, 21, 11), 24).unwrap(); assert_eq!(cidr.prefix(), 24); - let cidr = unsafe { Ipv4Network::new_unchecked(Ipv4Addr::new(0, 0, 0, 0), 33) }; - assert_eq!(cidr.prefix(), 33); + } + + #[test] + #[should_panic] + fn try_create_invalid_checked_v4() { + Ipv4Network::new_checked(Ipv4Addr::new(0, 0, 0, 0), 33).unwrap(); } #[test] From d7e69632fe93c56fb8eee7adb37a9c37d827db9b Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 3 Jan 2025 09:45:43 +0100 Subject: [PATCH 2/5] Replace `Ipv6Network::new_unchecked` with `Ipv6Network::new_checked` --- src/ipv6.rs | 57 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/ipv6.rs b/src/ipv6.rs index ea35cc3..1cd52bf 100644 --- a/src/ipv6.rs +++ b/src/ipv6.rs @@ -78,19 +78,17 @@ impl Ipv6Network { /// /// If the prefix is larger than 128 this will return an `IpNetworkError::InvalidPrefix`. pub const fn new(addr: Ipv6Addr, prefix: u8) -> Result { - if prefix > IPV6_BITS { - Err(IpNetworkError::InvalidPrefix) - } else { - Ok(Ipv6Network { addr, prefix }) + match Ipv6Network::new_checked(addr, prefix) { + Some(a) => Ok(a), + None => Err(IpNetworkError::InvalidPrefix), } } - /// Constructs without checking prefix a new `Ipv6Network` from any `Ipv6Addr, - /// and a prefix denoting the network size. + /// Constructs a new `Ipv6Network` from any `Ipv6Addr`, and a prefix denoting the network size. /// - /// # Safety - /// - /// The caller must ensure that the prefix is less than or equal to 128. + /// If the prefix is larger than 128 this will return `None`. This is useful in const contexts, + /// where [`Option::unwrap`] may be called to trigger a compile-time error in case the prefix + /// is an unexpected value. /// /// # Examples /// @@ -98,14 +96,30 @@ impl Ipv6Network { /// use std::net::Ipv6Addr; /// use ipnetwork::Ipv6Network; /// - /// let prefix = 64; - /// let addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); + /// const PREFIX: u8 = 64; + /// const ADDR: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); + /// + /// // Okay! + /// const NETWORK: Ipv6Network = Ipv6Network::new_checked(ADDR, PREFIX).unwrap(); + /// ``` + /// + /// ``` + /// use std::net::Ipv6Addr; + /// use ipnetwork::Ipv6Network; + /// + /// // Prefix is greater than 128. + /// const PREFIX: u8 = 128 + 1; + /// const ADDR: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); /// - /// debug_assert!(prefix <= 128); - /// let net = unsafe { Ipv6Network::new_unchecked(addr, prefix) }; + /// // Unwrap to get a compile-time error! + /// assert!(Ipv6Network::new_checked(ADDR, PREFIX).is_none()); /// ``` - pub const unsafe fn new_unchecked(addr: Ipv6Addr, prefix: u8) -> Ipv6Network { - Ipv6Network { addr, prefix } + pub const fn new_checked(addr: Ipv6Addr, prefix: u8) -> Option { + if prefix > IPV6_BITS { + None + } else { + Some(Ipv6Network { addr, prefix }) + } } /// Constructs a new `Ipv6Network` from a network address and a network mask. @@ -427,12 +441,15 @@ mod test { } #[test] - fn create_unchecked_v6() { - let cidr = unsafe { Ipv6Network::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24) }; + fn create_checked_v6() { + let cidr = Ipv6Network::new_checked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24).unwrap(); assert_eq!(cidr.prefix(), 24); - let cidr = - unsafe { Ipv6Network::new_unchecked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 129) }; - assert_eq!(cidr.prefix(), 129); + } + + #[test] + #[should_panic] + fn try_create_invalid_checked_v6() { + Ipv6Network::new_checked(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 129).unwrap(); } #[test] From 9fd7a3b77166056be498b04a90824c1e9d538bd4 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 3 Jan 2025 10:27:31 +0100 Subject: [PATCH 3/5] Update doc test for `Ipv{4,6}Network::new_checked` to use `should_panic` --- src/ipv4.rs | 10 ++++++---- src/ipv6.rs | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/ipv4.rs b/src/ipv4.rs index 4959044..ba0ea4b 100644 --- a/src/ipv4.rs +++ b/src/ipv4.rs @@ -89,10 +89,11 @@ impl Ipv4Network { /// const ADDR: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1); /// /// // Okay! - /// const NETWORK: Ipv4Network = Ipv4Network::new_checked(ADDR, PREFIX).unwrap(); + /// const NETWORK: Option = Ipv4Network::new_checked(ADDR, PREFIX); + /// assert_eq!(NETWORK.unwrap().prefix(), PREFIX); /// ``` /// - /// ``` + /// ```should_panic /// use std::net::Ipv4Addr; /// use ipnetwork::Ipv4Network; /// @@ -100,8 +101,9 @@ impl Ipv4Network { /// const PREFIX: u8 = 32 + 1; /// const ADDR: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1); /// - /// // Unwrap to get a compile-time error! - /// assert!(Ipv4Network::new_checked(ADDR, PREFIX).is_none()); + /// // This fails! + /// const NETWORK: Option = Ipv4Network::new_checked(ADDR, PREFIX); + /// assert_eq!(NETWORK.unwrap().prefix(), PREFIX); /// ``` pub const fn new_checked(addr: Ipv4Addr, prefix: u8) -> Option { if prefix > IPV4_BITS { diff --git a/src/ipv6.rs b/src/ipv6.rs index 1cd52bf..b69d904 100644 --- a/src/ipv6.rs +++ b/src/ipv6.rs @@ -100,10 +100,11 @@ impl Ipv6Network { /// const ADDR: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); /// /// // Okay! - /// const NETWORK: Ipv6Network = Ipv6Network::new_checked(ADDR, PREFIX).unwrap(); + /// const NETWORK: Option = Ipv6Network::new_checked(ADDR, PREFIX); + /// assert_eq!(NETWORK.unwrap().prefix(), PREFIX); /// ``` /// - /// ``` + /// ```should_panic /// use std::net::Ipv6Addr; /// use ipnetwork::Ipv6Network; /// @@ -111,8 +112,9 @@ impl Ipv6Network { /// const PREFIX: u8 = 128 + 1; /// const ADDR: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); /// - /// // Unwrap to get a compile-time error! - /// assert!(Ipv6Network::new_checked(ADDR, PREFIX).is_none()); + /// // This fails! + /// const NETWORK: Option = Ipv6Network::new_checked(ADDR, PREFIX); + /// assert_eq!(NETWORK.unwrap().prefix(), PREFIX); /// ``` pub const fn new_checked(addr: Ipv6Addr, prefix: u8) -> Option { if prefix > IPV6_BITS { From 21fd5edc744da5e906bf868d39fc375aa86a01a6 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 3 Jan 2025 11:22:17 +0100 Subject: [PATCH 4/5] Better demonstrate use of `Ipv{4,6}Network::new_checked` in const contexts --- src/ipv4.rs | 4 ++-- src/ipv6.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ipv4.rs b/src/ipv4.rs index ba0ea4b..58d834c 100644 --- a/src/ipv4.rs +++ b/src/ipv4.rs @@ -89,8 +89,8 @@ impl Ipv4Network { /// const ADDR: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1); /// /// // Okay! - /// const NETWORK: Option = Ipv4Network::new_checked(ADDR, PREFIX); - /// assert_eq!(NETWORK.unwrap().prefix(), PREFIX); + /// const NETWORK: Ipv4Network = Ipv4Network::new_checked(ADDR, PREFIX).unwrap(); + /// assert_eq!(NETWORK.prefix(), PREFIX); /// ``` /// /// ```should_panic diff --git a/src/ipv6.rs b/src/ipv6.rs index b69d904..fb12f1a 100644 --- a/src/ipv6.rs +++ b/src/ipv6.rs @@ -100,8 +100,8 @@ impl Ipv6Network { /// const ADDR: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); /// /// // Okay! - /// const NETWORK: Option = Ipv6Network::new_checked(ADDR, PREFIX); - /// assert_eq!(NETWORK.unwrap().prefix(), PREFIX); + /// const NETWORK: Ipv6Network = Ipv6Network::new_checked(ADDR, PREFIX).unwrap(); + /// assert_eq!(NETWORK.prefix(), PREFIX); /// ``` /// /// ```should_panic From 06c962f291cd057182b7ff65a2d55a78a8473d90 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 3 Jan 2025 14:56:58 +0100 Subject: [PATCH 5/5] Add back `unsafe_code` lint at the deny level --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 48df9c0..4931c0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ #![crate_type = "lib"] #![deny( missing_debug_implementations, + unsafe_code, unused_extern_crates, unused_import_braces )]