@@ -123,7 +123,33 @@ impl<'a> PyStringData<'a> {
123
123
124
124
/// Represents a Python `string` (a Unicode string object).
125
125
///
126
- /// This type is immutable.
126
+ /// This type is only seen inside PyO3's smart pointers as [`Py<PyString>`], [`Bound<'py, PyString>`],
127
+ /// and [`Borrowed<'a, 'py, PyString>`].
128
+ ///
129
+ /// All functionality on this type is implemented through the [`PyStringMethods`] trait.
130
+ ///
131
+ /// # Equality
132
+ ///
133
+ /// For convenience, [`Bound<'py, PyString>`] implements [`PartialEq<str>`] to allow comparing the
134
+ /// data in the Python string to a Rust UTF-8 string slice.
135
+ ///
136
+ /// This is not always the most appropriate way to compare Python strings, as Python string subclasses
137
+ /// may have different equality semantics. In situations where subclasses overriding equality might be
138
+ /// relevant, use [`PyAnyMethods::eq`], at cost of the additional overhead of a Python method call.
139
+ ///
140
+ /// ```rust
141
+ /// # use pyo3::prelude::*;
142
+ /// use pyo3::types::PyString;
143
+ ///
144
+ /// # Python::with_gil(|py| {
145
+ /// let py_string = PyString::new_bound(py, "foo");
146
+ /// // via PartialEq<str>
147
+ /// assert_eq!(py_string, "foo");
148
+ ///
149
+ /// // via Python equality
150
+ /// assert!(py_string.as_any().eq("foo").unwrap());
151
+ /// # });
152
+ /// ```
127
153
#[ repr( transparent) ]
128
154
pub struct PyString ( PyAny ) ;
129
155
@@ -490,6 +516,118 @@ impl IntoPy<Py<PyString>> for &'_ Py<PyString> {
490
516
}
491
517
}
492
518
519
+ /// Compares whether the data in the Python string is equal to the given UTF8.
520
+ ///
521
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
522
+ impl PartialEq < str > for Bound < ' _ , PyString > {
523
+ #[ inline]
524
+ fn eq ( & self , other : & str ) -> bool {
525
+ self . as_borrowed ( ) == * other
526
+ }
527
+ }
528
+
529
+ /// Compares whether the data in the Python string is equal to the given UTF8.
530
+ ///
531
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
532
+ impl PartialEq < & ' _ str > for Bound < ' _ , PyString > {
533
+ #[ inline]
534
+ fn eq ( & self , other : & & str ) -> bool {
535
+ self . as_borrowed ( ) == * * other
536
+ }
537
+ }
538
+
539
+ /// Compares whether the data in the Python string is equal to the given UTF8.
540
+ ///
541
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
542
+ impl PartialEq < Bound < ' _ , PyString > > for str {
543
+ #[ inline]
544
+ fn eq ( & self , other : & Bound < ' _ , PyString > ) -> bool {
545
+ * self == other. as_borrowed ( )
546
+ }
547
+ }
548
+
549
+ /// Compares whether the data in the Python string is equal to the given UTF8.
550
+ ///
551
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
552
+ impl PartialEq < & ' _ Bound < ' _ , PyString > > for str {
553
+ #[ inline]
554
+ fn eq ( & self , other : & & Bound < ' _ , PyString > ) -> bool {
555
+ * self == other. as_borrowed ( )
556
+ }
557
+ }
558
+
559
+ /// Compares whether the data in the Python string is equal to the given UTF8.
560
+ ///
561
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
562
+ impl PartialEq < Bound < ' _ , PyString > > for & ' _ str {
563
+ #[ inline]
564
+ fn eq ( & self , other : & Bound < ' _ , PyString > ) -> bool {
565
+ * * self == other. as_borrowed ( )
566
+ }
567
+ }
568
+
569
+ /// Compares whether the data in the Python string is equal to the given UTF8.
570
+ ///
571
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
572
+ impl PartialEq < str > for & ' _ Bound < ' _ , PyString > {
573
+ #[ inline]
574
+ fn eq ( & self , other : & str ) -> bool {
575
+ self . as_borrowed ( ) == other
576
+ }
577
+ }
578
+
579
+ /// Compares whether the data in the Python string is equal to the given UTF8.
580
+ ///
581
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
582
+ impl PartialEq < str > for Borrowed < ' _ , ' _ , PyString > {
583
+ #[ inline]
584
+ fn eq ( & self , other : & str ) -> bool {
585
+ #[ cfg( not( Py_3_13 ) ) ]
586
+ {
587
+ self . to_cow ( ) . map_or ( false , |s| s == other)
588
+ }
589
+
590
+ #[ cfg( Py_3_13 ) ]
591
+ unsafe {
592
+ ffi:: PyUnicode_EqualToUTF8AndSize (
593
+ self . as_ptr ( ) ,
594
+ other. as_ptr ( ) . cast ( ) ,
595
+ other. len ( ) as _ ,
596
+ ) == 1
597
+ }
598
+ }
599
+ }
600
+
601
+ /// Compares whether the data in the Python string is equal to the given UTF8.
602
+ ///
603
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
604
+ impl PartialEq < & str > for Borrowed < ' _ , ' _ , PyString > {
605
+ #[ inline]
606
+ fn eq ( & self , other : & & str ) -> bool {
607
+ * self == * * other
608
+ }
609
+ }
610
+
611
+ /// Compares whether the data in the Python string is equal to the given UTF8.
612
+ ///
613
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
614
+ impl PartialEq < Borrowed < ' _ , ' _ , PyString > > for str {
615
+ #[ inline]
616
+ fn eq ( & self , other : & Borrowed < ' _ , ' _ , PyString > ) -> bool {
617
+ other == self
618
+ }
619
+ }
620
+
621
+ /// Compares whether the data in the Python string is equal to the given UTF8.
622
+ ///
623
+ /// In some cases Python equality might be more appropriate; see the note on [`PyString`].
624
+ impl PartialEq < Borrowed < ' _ , ' _ , PyString > > for & ' _ str {
625
+ #[ inline]
626
+ fn eq ( & self , other : & Borrowed < ' _ , ' _ , PyString > ) -> bool {
627
+ other == self
628
+ }
629
+ }
630
+
493
631
#[ cfg( test) ]
494
632
mod tests {
495
633
use super :: * ;
@@ -708,15 +846,15 @@ mod tests {
708
846
fn test_intern_string ( ) {
709
847
Python :: with_gil ( |py| {
710
848
let py_string1 = PyString :: intern_bound ( py, "foo" ) ;
711
- assert_eq ! ( py_string1. to_cow ( ) . unwrap ( ) , "foo" ) ;
849
+ assert_eq ! ( py_string1, "foo" ) ;
712
850
713
851
let py_string2 = PyString :: intern_bound ( py, "foo" ) ;
714
- assert_eq ! ( py_string2. to_cow ( ) . unwrap ( ) , "foo" ) ;
852
+ assert_eq ! ( py_string2, "foo" ) ;
715
853
716
854
assert_eq ! ( py_string1. as_ptr( ) , py_string2. as_ptr( ) ) ;
717
855
718
856
let py_string3 = PyString :: intern_bound ( py, "bar" ) ;
719
- assert_eq ! ( py_string3. to_cow ( ) . unwrap ( ) , "bar" ) ;
857
+ assert_eq ! ( py_string3, "bar" ) ;
720
858
721
859
assert_ne ! ( py_string1. as_ptr( ) , py_string3. as_ptr( ) ) ;
722
860
} ) ;
@@ -762,4 +900,34 @@ mod tests {
762
900
assert_eq ! ( py_string. to_string_lossy( py) , "🐈 Hello ���World" ) ;
763
901
} )
764
902
}
903
+
904
+ #[ test]
905
+ fn test_comparisons ( ) {
906
+ Python :: with_gil ( |py| {
907
+ let s = "hello, world" ;
908
+ let py_string = PyString :: new_bound ( py, s) ;
909
+
910
+ assert_eq ! ( py_string, "hello, world" ) ;
911
+
912
+ assert_eq ! ( py_string, s) ;
913
+ assert_eq ! ( & py_string, s) ;
914
+ assert_eq ! ( s, py_string) ;
915
+ assert_eq ! ( s, & py_string) ;
916
+
917
+ assert_eq ! ( py_string, * s) ;
918
+ assert_eq ! ( & py_string, * s) ;
919
+ assert_eq ! ( * s, py_string) ;
920
+ assert_eq ! ( * s, & py_string) ;
921
+
922
+ let py_string = py_string. as_borrowed ( ) ;
923
+
924
+ assert_eq ! ( py_string, s) ;
925
+ assert_eq ! ( & py_string, s) ;
926
+ assert_eq ! ( s, py_string) ;
927
+ assert_eq ! ( s, & py_string) ;
928
+
929
+ assert_eq ! ( py_string, * s) ;
930
+ assert_eq ! ( * s, py_string) ;
931
+ } )
932
+ }
765
933
}
0 commit comments