comparison rust/pyo3-sharedref/src/lib.rs @ 52640:78b2894cd58c

rust-pyo3-sharedref: demonstrate SharedByPyObject borrow unsafety We take the existing doc-comment from rust-cpython and make it compile (validated by `cargo test`). With the added explanations, the ordinary comment was no longer useful, we could therefore remove it. The new explanation stresses that "not leaking the internal faked reference" is definitely not enough, because the problem is about *all references* that can be derived from it. We ended up duplicating the explanation, because that is a way to ensure that people do not miss it. Also, it was a bit misleading that the previous example was for `try_borrow_mut()`, so we made a similar, simpler one for `try_borrow()`.
author Georges Racinet <georges.racinet@cloudcrane.io>
date Mon, 16 Dec 2024 13:08:55 +0100
parents 76a0bdb0e4ca
children 189491cea922
comparison
equal deleted inserted replaced
52639:76a0bdb0e4ca 52640:78b2894cd58c
467 /// Even though [`SharedByPyObject`] tries to enforce the real lifetime of the 467 /// Even though [`SharedByPyObject`] tries to enforce the real lifetime of the
468 /// underlying object, the object having the artificial `'static` lifetime 468 /// underlying object, the object having the artificial `'static` lifetime
469 /// may be exposed to your Rust code. You must be careful to not make a bare 469 /// may be exposed to your Rust code. You must be careful to not make a bare
470 /// reference outlive the actual object lifetime. 470 /// reference outlive the actual object lifetime.
471 /// 471 ///
472 /// ```ignore 472 /// See [`Self::try_borrow_mut()`] for an example of the kind of trouble that
473 /// let mut outer = empty; 473 /// can arise.
474 /// let mut shared_iter = shared.map(py, |o| o.chars());
475 /// {
476 /// let mut iter = unsafe { shared_iter.try_borrow_mut(py) };
477 /// let inner = iter.next(); // Good, in borrow scope
478 /// outer = inner; // Bad, &'static T may outlive
479 /// }
480 /// ```
481 pub struct SharedByPyObject<T: ?Sized> { 474 pub struct SharedByPyObject<T: ?Sized> {
482 owner: PyObject, 475 owner: PyObject,
483 state: &'static PySharedState, 476 state: &'static PySharedState,
484 /// Generation counter of data `T` captured when SharedByPyObject is 477 /// Generation counter of data `T` captured when SharedByPyObject is
485 /// created. 478 /// created.
496 impl<T: ?Sized> SharedByPyObject<T> { 489 impl<T: ?Sized> SharedByPyObject<T> {
497 // No panicking version of borrow() and borrow_mut() are implemented 490 // No panicking version of borrow() and borrow_mut() are implemented
498 // because the underlying value is supposed to be mutated in Python 491 // because the underlying value is supposed to be mutated in Python
499 // world, and the Rust library designer can't prevent it. 492 // world, and the Rust library designer can't prevent it.
500 493
501 // try_borrow() and try_borrow_mut() are unsafe because self.data may
502 // have a function returning the inner &'static reference.
503 // If T is &'static U, its lifetime can be easily coerced to &'a U, but
504 // how could we do that for Whatever<'static> in general?
505
506 /// Immutably borrows the wrapped value. 494 /// Immutably borrows the wrapped value.
507 /// 495 ///
508 /// Borrowing fails if the underlying reference has been invalidated. 496 /// Borrowing fails if the underlying reference has been invalidated.
509 /// 497 ///
510 /// # Safety 498 /// # Safety
511 /// 499 ///
512 /// The lifetime of the innermost object is artificial. Do not obtain and 500 /// The lifetime of the innermost object is artificial. Do not obtain and
513 /// copy it out of the borrow scope. 501 /// copy it out of the borrow scope.
502 ///
503 /// The lifetime of the innermost object is artificial. Do not obtain and
504 /// copy it out of the borrow scope. More generally, the returned `&T`
505 /// may have a method returning an inner reference, which would typically
506 /// be `'static` and not safe without the `owner` Python object, so the
507 /// problem might be less obvious than in the example below.
508 ///
509 /// The following example does compile and illustrates the problem.
510 /// In this case, the data is a `Vec<String>` and the leaked reference
511 /// `&'static str`, which points to some element of the vector. This
512 /// illustrates that the leaks are not necessarily to the whole of the
513 /// shared data.
514 ///
515 /// ```no_run
516 /// # use pyo3::prelude::*;
517 /// # use pyo3_sharedref::PyShareable;
518 /// #[pyclass]
519 /// struct Owner {
520 /// value: PyShareable<Vec<String>>
521 /// }
522 ///
523 /// #[pymethods]
524 /// impl Owner {
525 /// #[new]
526 /// fn new(s: &str) -> Self {
527 /// let split: Vec<_> = s.split(' ').map(String::from).collect();
528 /// Self { value: split.into() }
529 /// }
530 /// }
531 ///
532 /// const EMPTY: &'static str = "";
533 ///
534 /// let mut outer = EMPTY;
535 /// Python::with_gil(|py| {
536 /// let owner = Bound::new(py, Owner::new("hello")).unwrap();
537 /// let shareable = &owner.borrow().value;
538 /// let shared = unsafe { shareable.share(&owner) };
539 /// {
540 /// let inner = unsafe { shared.try_borrow(py) }.unwrap();
541 /// outer = &inner[0]; // Bad, &'static str does outlive the scope
542 /// }
543 /// });
544 /// ```
514 pub unsafe fn try_borrow<'a>( 545 pub unsafe fn try_borrow<'a>(
515 &'a self, 546 &'a self,
516 py: Python<'a>, 547 py: Python<'a>,
517 ) -> PyResult<SharedByPyObjectRef<'a, T>> { 548 ) -> PyResult<SharedByPyObjectRef<'a, T>> {
518 self.validate_generation(py)?; 549 self.validate_generation(py)?;
524 555
525 /// Mutably borrows the wrapped value. 556 /// Mutably borrows the wrapped value.
526 /// 557 ///
527 /// Borrowing fails if the underlying reference has been invalidated. 558 /// Borrowing fails if the underlying reference has been invalidated.
528 /// 559 ///
529 /// Typically `T` is an iterator. If `T` is an immutable reference, 560 /// Typically `T` would be an iterator obtained by the [`Self::map`]
530 /// `get_mut()` is useless since the inner value can't be mutated. 561 /// method.
531 /// 562 ///
532 /// # Safety 563 /// # Safety
533 /// 564 ///
534 /// The lifetime of the innermost object is artificial. Do not obtain and 565 /// The lifetime of the innermost object is artificial. Do not obtain and
535 /// copy it out of the borrow scope. 566 /// copy it out of the borrow scope. More generally, the returned `&T`
567 /// may have a method returning an inner reference, which would typically
568 /// be `'static` and not safe without the `owner` Python object, so the
569 /// problem might be less obvious than in the example below.
570 ///
571 /// The following example does compile and illustrates the problem.
572 /// It is very close to the example given in [`Self::try_borrow`] because
573 /// the problem does not arise from the mutability of the reference
574 /// returned by this function.
575 ///
576 /// In this case, the data is a `Vec<String>` and the leaked reference
577 /// `&'static str`, which points to some element of the vector. This
578 /// illustrates that the leaks are not necessarily to the whole of the
579 /// shared data.
580 ///
581 /// ```no_run
582 /// # use pyo3::prelude::*;
583 /// # use pyo3_sharedref::PyShareable;
584 /// #[pyclass]
585 /// struct Owner {
586 /// value: PyShareable<Vec<String>>
587 /// }
588 ///
589 /// #[pymethods]
590 /// impl Owner {
591 /// #[new]
592 /// fn new(s: &str) -> Self {
593 /// let split: Vec<_> = s.split(' ').map(String::from).collect();
594 /// Self { value: split.into() }
595 /// }
596 /// }
597 ///
598 /// const EMPTY: &'static str = "";
599 ///
600 /// let mut outer = EMPTY;
601 /// Python::with_gil(|py| {
602 /// let owner = Bound::new(py, Owner::new("hello")).unwrap();
603 /// let shareable = &owner.borrow().value;
604 /// let shared = unsafe { shareable.share(&owner) };
605 /// let mut shared_iter = unsafe { shared.map(py, |o| o.iter()) };
606 /// {
607 /// let mut iter = unsafe {
608 /// shared_iter.try_borrow_mut(py)
609 /// }.unwrap();
610 /// let inner = iter.next().unwrap(); // Good, in borrow scope
611 /// outer = inner; // Bad, &'static str does outlive the scope
612 /// }
613 /// });
614 /// ```
536 pub unsafe fn try_borrow_mut<'a>( 615 pub unsafe fn try_borrow_mut<'a>(
537 &'a mut self, 616 &'a mut self,
538 py: Python<'a>, 617 py: Python<'a>,
539 ) -> PyResult<SharedByPyObjectRefMut<'a, T>> { 618 ) -> PyResult<SharedByPyObjectRefMut<'a, T>> {
540 self.validate_generation(py)?; 619 self.validate_generation(py)?;