about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2025-06-24 15:39:37 +0200
committerGitHub <noreply@github.com>2025-06-24 15:39:37 +0200
commit30550c048d0d15a91ecd678f265d331531d49f7b (patch)
treefa03e3c3a59ef7001772f725160326b0643ce6cb
parent36b21637e93b038453924d3c66821089e71d8baa (diff)
parent817b0932fa66ee8fd6e695f9ec74804a6c360269 (diff)
downloadrust-30550c048d0d15a91ecd678f265d331531d49f7b.tar.gz
rust-30550c048d0d15a91ecd678f265d331531d49f7b.zip
Rollup merge of #137268 - bjoernager:c-string-eq-c-str, r=Amanieu
Allow comparisons between `CStr`, `CString`, and `Cow<CStr>`.

Closes: #137265

This PR adds the trait implementations proposed in the [ACP](https://github.com/rust-lang/libs-team/issues/517/) under the `c_string_eq_c_str` feature gate:

```rust
// core::ffi

impl PartialEq<&Self> for CStr;

impl PartialEq<CString> for CStr;

impl PartialEq<Cow<'_, Self>> for CStr;

// alloc::ffi

impl PartialEq<CStr> for CString;

impl PartialEq<&CStr> for CString;

impl PartialEq<Cow<'_, CStr>> for CString;

// alloc::borrow

impl PartialEq<CStr> for Cow<'_, CStr>;

impl PartialEq<&CStr> for Cow<'_, CStr>;

impl PartialEq<CString> for Cow<'_, CStr>;
```

As I understand it, stable traits cannot be unstably implemented for stable types, and we would thereby be forced to skip the FCP and directly stabilise these implementations (as is done in this PR).

(`@joshtriplett` mentioned that Crater may have to be run).
-rw-r--r--library/alloc/src/ffi/c_str.rs109
-rw-r--r--library/core/src/ffi/c_str.rs14
2 files changed, 123 insertions, 0 deletions
diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs
index 48849bf7536..93bdad75380 100644
--- a/library/alloc/src/ffi/c_str.rs
+++ b/library/alloc/src/ffi/c_str.rs
@@ -1099,6 +1099,46 @@ impl From<&CStr> for CString {
     }
 }
 
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CStr> for CString {
+    #[inline]
+    fn eq(&self, other: &CStr) -> bool {
+        **self == *other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CStr) -> bool {
+        **self != *other
+    }
+}
+
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<&CStr> for CString {
+    #[inline]
+    fn eq(&self, other: &&CStr) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &&CStr) -> bool {
+        **self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<Cow<'_, CStr>> for CString {
+    #[inline]
+    fn eq(&self, other: &Cow<'_, CStr>) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &Cow<'_, CStr>) -> bool {
+        **self != **other
+    }
+}
+
 #[stable(feature = "cstring_asref", since = "1.7.0")]
 impl ops::Index<ops::RangeFull> for CString {
     type Output = CStr;
@@ -1181,6 +1221,75 @@ impl CStr {
     }
 }
 
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CString> for CStr {
+    #[inline]
+    fn eq(&self, other: &CString) -> bool {
+        *self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CString) -> bool {
+        *self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<Cow<'_, Self>> for CStr {
+    #[inline]
+    fn eq(&self, other: &Cow<'_, Self>) -> bool {
+        *self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &Cow<'_, Self>) -> bool {
+        *self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CStr> for Cow<'_, CStr> {
+    #[inline]
+    fn eq(&self, other: &CStr) -> bool {
+        **self == *other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CStr) -> bool {
+        **self != *other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<&CStr> for Cow<'_, CStr> {
+    #[inline]
+    fn eq(&self, other: &&CStr) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &&CStr) -> bool {
+        **self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CString> for Cow<'_, CStr> {
+    #[inline]
+    fn eq(&self, other: &CString) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CString) -> bool {
+        **self != **other
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl core::error::Error for NulError {
     #[allow(deprecated)]
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index f7a21072f53..1d2e13db218 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -660,6 +660,19 @@ impl CStr {
     }
 }
 
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<&Self> for CStr {
+    #[inline]
+    fn eq(&self, other: &&Self) -> bool {
+        *self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &&Self) -> bool {
+        *self != **other
+    }
+}
+
 // `.to_bytes()` representations are compared instead of the inner `[c_char]`s,
 // because `c_char` is `i8` (not `u8`) on some platforms.
 // That is why this is implemented manually and not derived.
@@ -670,6 +683,7 @@ impl PartialOrd for CStr {
         self.to_bytes().partial_cmp(&other.to_bytes())
     }
 }
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Ord for CStr {
     #[inline]