1#[cfg(feature = "experimental-inspect")]
4use crate::inspect::PyStaticExpr;
5#[cfg(feature = "experimental-inspect")]
6use crate::type_hint_union;
7use crate::{
8 types::{
9 bytearray::PyByteArrayMethods, bytes::PyBytesMethods, string::PyStringMethods, PyByteArray,
10 PyBytes, PyString, PyTuple,
11 },
12 Borrowed, Bound, CastError, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyTypeInfo, Python,
13};
14use std::{borrow::Borrow, convert::Infallible, ops::Deref, ptr::NonNull, sync::Arc};
15
16#[cfg_attr(feature = "py-clone", derive(Clone))]
23pub struct PyBackedStr {
24 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
25 storage: Py<PyString>,
26 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
27 storage: Py<PyBytes>,
28 data: NonNull<str>,
29}
30
31impl PyBackedStr {
32 #[inline]
36 pub fn clone_ref(&self, py: Python<'_>) -> Self {
37 Self {
38 storage: self.storage.clone_ref(py),
39 data: self.data,
40 }
41 }
42
43 #[inline]
45 pub fn as_str(&self) -> &str {
46 unsafe { self.data.as_ref() }
48 }
49
50 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
54 #[inline]
55 pub fn as_py_str(&self) -> &Py<PyString> {
56 &self.storage
57 }
58}
59
60impl Deref for PyBackedStr {
61 type Target = str;
62 #[inline]
63 fn deref(&self) -> &str {
64 self.as_str()
65 }
66}
67
68impl AsRef<str> for PyBackedStr {
69 #[inline]
70 fn as_ref(&self) -> &str {
71 self
72 }
73}
74
75impl AsRef<[u8]> for PyBackedStr {
76 #[inline]
77 fn as_ref(&self) -> &[u8] {
78 self.as_bytes()
79 }
80}
81
82impl Borrow<str> for PyBackedStr {
83 #[inline]
84 fn borrow(&self) -> &str {
85 self
86 }
87}
88
89unsafe impl Send for PyBackedStr {}
92unsafe impl Sync for PyBackedStr {}
93
94impl std::fmt::Display for PyBackedStr {
95 #[inline]
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 self.deref().fmt(f)
98 }
99}
100
101impl_traits!(PyBackedStr, str);
102
103impl TryFrom<Bound<'_, PyString>> for PyBackedStr {
104 type Error = PyErr;
105 fn try_from(py_string: Bound<'_, PyString>) -> Result<Self, Self::Error> {
106 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
107 {
108 let s = py_string.to_str()?;
109 let data = NonNull::from(s);
110 Ok(Self {
111 storage: py_string.unbind(),
112 data,
113 })
114 }
115 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
116 {
117 let bytes = py_string.encode_utf8()?;
118 let s = unsafe { std::str::from_utf8_unchecked(bytes.as_bytes()) };
119 let data = NonNull::from(s);
120 Ok(Self {
121 storage: bytes.unbind(),
122 data,
123 })
124 }
125 }
126}
127
128impl FromPyObject<'_, '_> for PyBackedStr {
129 type Error = PyErr;
130
131 #[cfg(feature = "experimental-inspect")]
132 const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
133
134 #[inline]
135 fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
136 let py_string = obj.cast::<PyString>()?.to_owned();
137 Self::try_from(py_string)
138 }
139}
140
141impl<'py> IntoPyObject<'py> for PyBackedStr {
142 type Target = PyString;
143 type Output = Bound<'py, Self::Target>;
144 type Error = Infallible;
145
146 #[cfg(feature = "experimental-inspect")]
147 const OUTPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
148
149 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
150 #[inline]
151 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
152 Ok(self.storage.into_bound(py))
153 }
154
155 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
156 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
157 Ok(PyString::new(py, &self))
158 }
159}
160
161impl<'py> IntoPyObject<'py> for &PyBackedStr {
162 type Target = PyString;
163 type Output = Bound<'py, Self::Target>;
164 type Error = Infallible;
165
166 #[cfg(feature = "experimental-inspect")]
167 const OUTPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
168
169 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
170 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
171 Ok(self.storage.bind(py).to_owned())
172 }
173
174 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
175 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
176 Ok(PyString::new(py, self))
177 }
178}
179
180#[cfg_attr(feature = "py-clone", derive(Clone))]
184pub struct PyBackedBytes {
185 storage: PyBackedBytesStorage,
186 data: NonNull<[u8]>,
187}
188
189#[cfg_attr(feature = "py-clone", derive(Clone))]
190enum PyBackedBytesStorage {
191 Python(Py<PyBytes>),
192 Rust(Arc<[u8]>),
193}
194
195impl PyBackedBytes {
196 pub fn clone_ref(&self, py: Python<'_>) -> Self {
200 Self {
201 storage: match &self.storage {
202 PyBackedBytesStorage::Python(bytes) => {
203 PyBackedBytesStorage::Python(bytes.clone_ref(py))
204 }
205 PyBackedBytesStorage::Rust(bytes) => PyBackedBytesStorage::Rust(bytes.clone()),
206 },
207 data: self.data,
208 }
209 }
210}
211
212impl Deref for PyBackedBytes {
213 type Target = [u8];
214 fn deref(&self) -> &[u8] {
215 unsafe { self.data.as_ref() }
217 }
218}
219
220impl AsRef<[u8]> for PyBackedBytes {
221 fn as_ref(&self) -> &[u8] {
222 self
223 }
224}
225
226unsafe impl Send for PyBackedBytes {}
229unsafe impl Sync for PyBackedBytes {}
230
231impl<const N: usize> PartialEq<[u8; N]> for PyBackedBytes {
232 fn eq(&self, other: &[u8; N]) -> bool {
233 self.deref() == other
234 }
235}
236
237impl<const N: usize> PartialEq<PyBackedBytes> for [u8; N] {
238 fn eq(&self, other: &PyBackedBytes) -> bool {
239 self == other.deref()
240 }
241}
242
243impl<const N: usize> PartialEq<&[u8; N]> for PyBackedBytes {
244 fn eq(&self, other: &&[u8; N]) -> bool {
245 self.deref() == *other
246 }
247}
248
249impl<const N: usize> PartialEq<PyBackedBytes> for &[u8; N] {
250 fn eq(&self, other: &PyBackedBytes) -> bool {
251 self == &other.deref()
252 }
253}
254
255impl_traits!(PyBackedBytes, [u8]);
256
257impl From<Bound<'_, PyBytes>> for PyBackedBytes {
258 fn from(py_bytes: Bound<'_, PyBytes>) -> Self {
259 let b = py_bytes.as_bytes();
260 let data = NonNull::from(b);
261 Self {
262 storage: PyBackedBytesStorage::Python(py_bytes.to_owned().unbind()),
263 data,
264 }
265 }
266}
267
268impl From<Bound<'_, PyByteArray>> for PyBackedBytes {
269 fn from(py_bytearray: Bound<'_, PyByteArray>) -> Self {
270 let s = Arc::<[u8]>::from(py_bytearray.to_vec());
271 let data = NonNull::from(s.as_ref());
272 Self {
273 storage: PyBackedBytesStorage::Rust(s),
274 data,
275 }
276 }
277}
278
279impl<'a, 'py> FromPyObject<'a, 'py> for PyBackedBytes {
280 type Error = CastError<'a, 'py>;
281
282 #[cfg(feature = "experimental-inspect")]
283 const INPUT_TYPE: PyStaticExpr = type_hint_union!(PyBytes::TYPE_HINT, PyByteArray::TYPE_HINT);
284
285 fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
286 if let Ok(bytes) = obj.cast::<PyBytes>() {
287 Ok(Self::from(bytes.to_owned()))
288 } else if let Ok(bytearray) = obj.cast::<PyByteArray>() {
289 Ok(Self::from(bytearray.to_owned()))
290 } else {
291 Err(CastError::new(
292 obj,
293 PyTuple::new(
294 obj.py(),
295 [
296 PyBytes::type_object(obj.py()),
297 PyByteArray::type_object(obj.py()),
298 ],
299 )
300 .unwrap()
301 .into_any(),
302 ))
303 }
304 }
305}
306
307impl<'py> IntoPyObject<'py> for PyBackedBytes {
308 type Target = PyBytes;
309 type Output = Bound<'py, Self::Target>;
310 type Error = Infallible;
311
312 #[cfg(feature = "experimental-inspect")]
313 const OUTPUT_TYPE: PyStaticExpr = PyBytes::TYPE_HINT;
314
315 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
316 match self.storage {
317 PyBackedBytesStorage::Python(bytes) => Ok(bytes.into_bound(py)),
318 PyBackedBytesStorage::Rust(bytes) => Ok(PyBytes::new(py, &bytes)),
319 }
320 }
321}
322
323impl<'py> IntoPyObject<'py> for &PyBackedBytes {
324 type Target = PyBytes;
325 type Output = Bound<'py, Self::Target>;
326 type Error = Infallible;
327
328 #[cfg(feature = "experimental-inspect")]
329 const OUTPUT_TYPE: PyStaticExpr = PyBytes::TYPE_HINT;
330
331 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
332 match &self.storage {
333 PyBackedBytesStorage::Python(bytes) => Ok(bytes.bind(py).clone()),
334 PyBackedBytesStorage::Rust(bytes) => Ok(PyBytes::new(py, bytes)),
335 }
336 }
337}
338
339macro_rules! impl_traits {
340 ($slf:ty, $equiv:ty) => {
341 impl std::fmt::Debug for $slf {
342 #[inline]
343 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344 self.deref().fmt(f)
345 }
346 }
347
348 impl PartialEq for $slf {
349 #[inline]
350 fn eq(&self, other: &Self) -> bool {
351 self.deref() == other.deref()
352 }
353 }
354
355 impl PartialEq<$equiv> for $slf {
356 #[inline]
357 fn eq(&self, other: &$equiv) -> bool {
358 self.deref() == other
359 }
360 }
361
362 impl PartialEq<&$equiv> for $slf {
363 #[inline]
364 fn eq(&self, other: &&$equiv) -> bool {
365 self.deref() == *other
366 }
367 }
368
369 impl PartialEq<$slf> for $equiv {
370 #[inline]
371 fn eq(&self, other: &$slf) -> bool {
372 self == other.deref()
373 }
374 }
375
376 impl PartialEq<$slf> for &$equiv {
377 #[inline]
378 fn eq(&self, other: &$slf) -> bool {
379 self == &other.deref()
380 }
381 }
382
383 impl Eq for $slf {}
384
385 impl PartialOrd for $slf {
386 #[inline]
387 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
388 Some(self.cmp(other))
389 }
390 }
391
392 impl PartialOrd<$equiv> for $slf {
393 #[inline]
394 fn partial_cmp(&self, other: &$equiv) -> Option<std::cmp::Ordering> {
395 self.deref().partial_cmp(other)
396 }
397 }
398
399 impl PartialOrd<$slf> for $equiv {
400 #[inline]
401 fn partial_cmp(&self, other: &$slf) -> Option<std::cmp::Ordering> {
402 self.partial_cmp(other.deref())
403 }
404 }
405
406 impl Ord for $slf {
407 #[inline]
408 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
409 self.deref().cmp(other.deref())
410 }
411 }
412
413 impl std::hash::Hash for $slf {
414 #[inline]
415 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
416 self.deref().hash(state)
417 }
418 }
419 };
420}
421use impl_traits;
422
423#[cfg(test)]
424mod test {
425 use super::*;
426 use crate::impl_::pyclass::{value_of, IsSend, IsSync};
427 use crate::types::PyAnyMethods as _;
428 use crate::{IntoPyObject, Python};
429 use std::collections::hash_map::DefaultHasher;
430 use std::hash::{Hash, Hasher};
431
432 #[test]
433 fn py_backed_str_empty() {
434 Python::attach(|py| {
435 let s = PyString::new(py, "");
436 let py_backed_str = s.extract::<PyBackedStr>().unwrap();
437 assert_eq!(&*py_backed_str, "");
438 });
439 }
440
441 #[test]
442 fn py_backed_str() {
443 Python::attach(|py| {
444 let s = PyString::new(py, "hello");
445 let py_backed_str = s.extract::<PyBackedStr>().unwrap();
446 assert_eq!(&*py_backed_str, "hello");
447 });
448 }
449
450 #[test]
451 fn py_backed_str_try_from() {
452 Python::attach(|py| {
453 let s = PyString::new(py, "hello");
454 let py_backed_str = PyBackedStr::try_from(s).unwrap();
455 assert_eq!(&*py_backed_str, "hello");
456 });
457 }
458
459 #[test]
460 fn py_backed_str_into_pyobject() {
461 Python::attach(|py| {
462 let orig_str = PyString::new(py, "hello");
463 let py_backed_str = orig_str.extract::<PyBackedStr>().unwrap();
464 let new_str = py_backed_str.into_pyobject(py).unwrap();
465 assert_eq!(new_str.extract::<PyBackedStr>().unwrap(), "hello");
466 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
467 assert!(new_str.is(&orig_str));
468 });
469 }
470
471 #[test]
472 fn py_backed_bytes_empty() {
473 Python::attach(|py| {
474 let b = PyBytes::new(py, b"");
475 let py_backed_bytes = b.extract::<PyBackedBytes>().unwrap();
476 assert_eq!(&*py_backed_bytes, b"");
477 });
478 }
479
480 #[test]
481 fn py_backed_bytes() {
482 Python::attach(|py| {
483 let b = PyBytes::new(py, b"abcde");
484 let py_backed_bytes = b.extract::<PyBackedBytes>().unwrap();
485 assert_eq!(&*py_backed_bytes, b"abcde");
486 });
487 }
488
489 #[test]
490 fn py_backed_bytes_from_bytes() {
491 Python::attach(|py| {
492 let b = PyBytes::new(py, b"abcde");
493 let py_backed_bytes = PyBackedBytes::from(b);
494 assert_eq!(&*py_backed_bytes, b"abcde");
495 });
496 }
497
498 #[test]
499 fn py_backed_bytes_from_bytearray() {
500 Python::attach(|py| {
501 let b = PyByteArray::new(py, b"abcde");
502 let py_backed_bytes = PyBackedBytes::from(b);
503 assert_eq!(&*py_backed_bytes, b"abcde");
504 });
505 }
506
507 #[test]
508 fn py_backed_bytes_into_pyobject() {
509 Python::attach(|py| {
510 let orig_bytes = PyBytes::new(py, b"abcde");
511 let py_backed_bytes = PyBackedBytes::from(orig_bytes.clone());
512 assert!((&py_backed_bytes)
513 .into_pyobject(py)
514 .unwrap()
515 .is(&orig_bytes));
516 });
517 }
518
519 #[test]
520 fn rust_backed_bytes_into_pyobject() {
521 Python::attach(|py| {
522 let orig_bytes = PyByteArray::new(py, b"abcde");
523 let rust_backed_bytes = PyBackedBytes::from(orig_bytes);
524 assert!(matches!(
525 rust_backed_bytes.storage,
526 PyBackedBytesStorage::Rust(_)
527 ));
528 let to_object = (&rust_backed_bytes).into_pyobject(py).unwrap();
529 assert!(&to_object.is_exact_instance_of::<PyBytes>());
530 assert_eq!(&to_object.extract::<PyBackedBytes>().unwrap(), b"abcde");
531 });
532 }
533
534 #[test]
535 fn test_backed_types_send_sync() {
536 assert!(value_of!(IsSend, PyBackedStr));
537 assert!(value_of!(IsSync, PyBackedStr));
538
539 assert!(value_of!(IsSend, PyBackedBytes));
540 assert!(value_of!(IsSync, PyBackedBytes));
541 }
542
543 #[cfg(feature = "py-clone")]
544 #[test]
545 fn test_backed_str_clone() {
546 Python::attach(|py| {
547 let s1: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
548 let s2 = s1.clone();
549 assert_eq!(s1, s2);
550
551 drop(s1);
552 assert_eq!(s2, "hello");
553 });
554 }
555
556 #[test]
557 fn test_backed_str_clone_ref() {
558 Python::attach(|py| {
559 let s1: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
560 let s2 = s1.clone_ref(py);
561 assert_eq!(s1, s2);
562 assert!(s1.storage.is(&s2.storage));
563
564 drop(s1);
565 assert_eq!(s2, "hello");
566 });
567 }
568
569 #[test]
570 fn test_backed_str_eq() {
571 Python::attach(|py| {
572 let s1: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
573 let s2: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
574 assert_eq!(s1, "hello");
575 assert_eq!(s1, s2);
576
577 let s3: PyBackedStr = PyString::new(py, "abcde").try_into().unwrap();
578 assert_eq!("abcde", s3);
579 assert_ne!(s1, s3);
580 });
581 }
582
583 #[test]
584 fn test_backed_str_hash() {
585 Python::attach(|py| {
586 let h = {
587 let mut hasher = DefaultHasher::new();
588 "abcde".hash(&mut hasher);
589 hasher.finish()
590 };
591
592 let s1: PyBackedStr = PyString::new(py, "abcde").try_into().unwrap();
593 let h1 = {
594 let mut hasher = DefaultHasher::new();
595 s1.hash(&mut hasher);
596 hasher.finish()
597 };
598
599 assert_eq!(h, h1);
600 });
601 }
602
603 #[test]
604 fn test_backed_str_ord() {
605 Python::attach(|py| {
606 let mut a = vec!["a", "c", "d", "b", "f", "g", "e"];
607 let mut b = a
608 .iter()
609 .map(|s| PyString::new(py, s).try_into().unwrap())
610 .collect::<Vec<PyBackedStr>>();
611
612 a.sort();
613 b.sort();
614
615 assert_eq!(a, b);
616 })
617 }
618
619 #[test]
620 fn test_backed_str_map_key() {
621 Python::attach(|py| {
622 use std::collections::HashMap;
623
624 let mut map: HashMap<PyBackedStr, usize> = HashMap::new();
625 let s: PyBackedStr = PyString::new(py, "key1").try_into().unwrap();
626
627 map.insert(s, 1);
628
629 assert_eq!(map.get("key1"), Some(&1));
630 });
631 }
632
633 #[test]
634 fn test_backed_str_as_str() {
635 Python::attach(|py| {
636 let s: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
637 assert_eq!(s.as_str(), "hello");
638 });
639 }
640
641 #[test]
642 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
643 fn test_backed_str_as_py_str() {
644 Python::attach(|py| {
645 let s: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
646 let py_str = s.as_py_str().bind(py);
647 assert!(py_str.is(&s.storage));
648 assert_eq!(py_str.to_str().unwrap(), "hello");
649 });
650 }
651
652 #[cfg(feature = "py-clone")]
653 #[test]
654 fn test_backed_bytes_from_bytes_clone() {
655 Python::attach(|py| {
656 let b1: PyBackedBytes = PyBytes::new(py, b"abcde").into();
657 let b2 = b1.clone();
658 assert_eq!(b1, b2);
659
660 drop(b1);
661 assert_eq!(b2, b"abcde");
662 });
663 }
664
665 #[test]
666 fn test_backed_bytes_from_bytes_clone_ref() {
667 Python::attach(|py| {
668 let b1: PyBackedBytes = PyBytes::new(py, b"abcde").into();
669 let b2 = b1.clone_ref(py);
670 assert_eq!(b1, b2);
671 let (PyBackedBytesStorage::Python(s1), PyBackedBytesStorage::Python(s2)) =
672 (&b1.storage, &b2.storage)
673 else {
674 panic!("Expected Python-backed bytes");
675 };
676 assert!(s1.is(s2));
677
678 drop(b1);
679 assert_eq!(b2, b"abcde");
680 });
681 }
682
683 #[cfg(feature = "py-clone")]
684 #[test]
685 fn test_backed_bytes_from_bytearray_clone() {
686 Python::attach(|py| {
687 let b1: PyBackedBytes = PyByteArray::new(py, b"abcde").into();
688 let b2 = b1.clone();
689 assert_eq!(b1, b2);
690
691 drop(b1);
692 assert_eq!(b2, b"abcde");
693 });
694 }
695
696 #[test]
697 fn test_backed_bytes_from_bytearray_clone_ref() {
698 Python::attach(|py| {
699 let b1: PyBackedBytes = PyByteArray::new(py, b"abcde").into();
700 let b2 = b1.clone_ref(py);
701 assert_eq!(b1, b2);
702 let (PyBackedBytesStorage::Rust(s1), PyBackedBytesStorage::Rust(s2)) =
703 (&b1.storage, &b2.storage)
704 else {
705 panic!("Expected Rust-backed bytes");
706 };
707 assert!(Arc::ptr_eq(s1, s2));
708
709 drop(b1);
710 assert_eq!(b2, b"abcde");
711 });
712 }
713
714 #[test]
715 fn test_backed_bytes_eq() {
716 Python::attach(|py| {
717 let b1: PyBackedBytes = PyBytes::new(py, b"abcde").into();
718 let b2: PyBackedBytes = PyByteArray::new(py, b"abcde").into();
719
720 assert_eq!(b1, b"abcde");
721 assert_eq!(b1, b2);
722
723 let b3: PyBackedBytes = PyBytes::new(py, b"hello").into();
724 assert_eq!(b"hello", b3);
725 assert_ne!(b1, b3);
726 });
727 }
728
729 #[test]
730 fn test_backed_bytes_hash() {
731 Python::attach(|py| {
732 let h = {
733 let mut hasher = DefaultHasher::new();
734 b"abcde".hash(&mut hasher);
735 hasher.finish()
736 };
737
738 let b1: PyBackedBytes = PyBytes::new(py, b"abcde").into();
739 let h1 = {
740 let mut hasher = DefaultHasher::new();
741 b1.hash(&mut hasher);
742 hasher.finish()
743 };
744
745 let b2: PyBackedBytes = PyByteArray::new(py, b"abcde").into();
746 let h2 = {
747 let mut hasher = DefaultHasher::new();
748 b2.hash(&mut hasher);
749 hasher.finish()
750 };
751
752 assert_eq!(h, h1);
753 assert_eq!(h, h2);
754 });
755 }
756
757 #[test]
758 fn test_backed_bytes_ord() {
759 Python::attach(|py| {
760 let mut a = vec![b"a", b"c", b"d", b"b", b"f", b"g", b"e"];
761 let mut b = a
762 .iter()
763 .map(|&b| PyBytes::new(py, b).into())
764 .collect::<Vec<PyBackedBytes>>();
765
766 a.sort();
767 b.sort();
768
769 assert_eq!(a, b);
770 })
771 }
772}