diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f04a7dd..814e07d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed * Implementing the Using the `gc` parameter for `pyclass` (e.g. `#[pyclass(gc)]`) without implementing the `class::PyGCProtocol` trait is now a compile-time error. Failing to implement this trait could lead to segfaults. [#532](https://github.com/PyO3/pyo3/pull/532) + * `PyByteArray::data` has been replaced with `PyDataArray::to_vec` because returning a `&[u8]` is unsound. (See [this comment](https://github.com/PyO3/pyo3/issues/373#issuecomment-512332696) for a great write-up for why that was unsound) ## [0.7.0] - 2018-05-26 diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index 690f0f9d..f62f4321 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -18,8 +18,6 @@ pyobject_native_type!(PyByteArray, ffi::PyByteArray_Type, ffi::PyByteArray_Check impl PyByteArray { /// Creates a new Python bytearray object. /// The byte string is initialized by copying the data from the `&[u8]`. - /// - /// Panics if out of memory. pub fn new<'p>(py: Python<'p>, src: &[u8]) -> &'p PyByteArray { let ptr = src.as_ptr() as *const c_char; let len = src.len() as ffi::Py_ssize_t; @@ -47,16 +45,37 @@ impl PyByteArray { self.len() == 0 } - /// Gets the Python bytearray data as byte slice. - pub fn data(&self) -> &[u8] { - unsafe { + /// Copies the contents of the bytearray to a rust vector + /// + /// # Example + /// + /// ``` + /// # use pyo3::prelude::*; + /// # use pyo3::types::PyByteArray; + /// # use pyo3::types::IntoPyDict; + /// # let gil = GILGuard::acquire(); + /// # let py = gil.python(); + /// # + /// let bytearray = PyByteArray::new(py, b"Hello World."); + /// let mut copied_message = bytearray.data(); + /// assert_eq!(b"Hello World.", copied_message.as_slice()); + /// + /// copied_message[11] = b'!'; + /// assert_eq!(b"Hello World!", copied_message.as_slice()); + /// + /// let locals = [("bytearray", bytearray)].into_py_dict(py); + /// py.run("assert bytearray == b'Hello World.'", None, Some(locals)).unwrap(); + /// ``` + pub fn to_vec(&self) -> Vec { + let slice = unsafe { let buffer = ffi::PyByteArray_AsString(self.0.as_ptr()) as *mut u8; let length = ffi::PyByteArray_Size(self.0.as_ptr()) as usize; slice::from_raw_parts_mut(buffer, length) - } + }; + slice.to_vec() } - /// Resize bytearray object. + /// Resize bytearray object to `len`. pub fn resize(&self, len: usize) -> PyResult<()> { unsafe { let result = ffi::PyByteArray_Resize(self.0.as_ptr(), len as ffi::Py_ssize_t); @@ -84,13 +103,13 @@ mod test { let src = b"Hello Python"; let bytearray = PyByteArray::new(py, src); assert_eq!(src.len(), bytearray.len()); - assert_eq!(src, bytearray.data()); + assert_eq!(src, bytearray.data().as_slice()); let ba: PyObject = bytearray.into(); let bytearray = PyByteArray::from(py, &ba).unwrap(); assert_eq!(src.len(), bytearray.len()); - assert_eq!(src, bytearray.data()); + assert_eq!(src, bytearray.data().as_slice()); bytearray.resize(20).unwrap(); assert_eq!(20, bytearray.len());