@@ -249,7 +249,7 @@ impl<T, D> PyArray<T, D> {
249
249
/// ```
250
250
/// use numpy::PyArray3;
251
251
/// pyo3::Python::with_gil(|py| {
252
- /// let arr = PyArray3::<f64>::new (py, [4, 5, 6], false);
252
+ /// let arr = PyArray3::<f64>::zeros (py, [4, 5, 6], false);
253
253
/// assert_eq!(arr.ndim(), 3);
254
254
/// });
255
255
/// ```
@@ -266,7 +266,7 @@ impl<T, D> PyArray<T, D> {
266
266
/// ```
267
267
/// use numpy::PyArray3;
268
268
/// pyo3::Python::with_gil(|py| {
269
- /// let arr = PyArray3::<f64>::new (py, [4, 5, 6], false);
269
+ /// let arr = PyArray3::<f64>::zeros (py, [4, 5, 6], false);
270
270
/// assert_eq!(arr.strides(), &[240, 48, 8]);
271
271
/// });
272
272
/// ```
@@ -287,7 +287,7 @@ impl<T, D> PyArray<T, D> {
287
287
/// ```
288
288
/// use numpy::PyArray3;
289
289
/// pyo3::Python::with_gil(|py| {
290
- /// let arr = PyArray3::<f64>::new (py, [4, 5, 6], false);
290
+ /// let arr = PyArray3::<f64>::zeros (py, [4, 5, 6], false);
291
291
/// assert_eq!(arr.shape(), &[4, 5, 6]);
292
292
/// });
293
293
/// ```
@@ -371,20 +371,46 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
371
371
///
372
372
/// If `is_fortran == true`, returns Fortran-order array. Else, returns C-order array.
373
373
///
374
+ /// # Safety
375
+ ///
376
+ /// The returned array will always be safe to be dropped as the elements must either
377
+ /// be trivially copyable or have `DATA_TYPE == DataType::Object`, i.e. be pointers
378
+ /// into Python's heap, which NumPy will automatically zero-initialize.
379
+ ///
380
+ /// However, the elements themselves will not be valid and should only be accessed
381
+ /// via raw pointers obtained via [uget_raw](#method.uget_raw).
382
+ ///
383
+ /// All methods which produce references to the elements invoke undefined behaviour.
384
+ /// In particular, zero-initialized pointers are _not_ valid instances of `PyObject`.
385
+ ///
374
386
/// # Example
375
387
/// ```
376
388
/// use numpy::PyArray3;
389
+ ///
377
390
/// pyo3::Python::with_gil(|py| {
378
- /// let arr = PyArray3::<i32>::new(py, [4, 5, 6], false);
391
+ /// let arr = unsafe {
392
+ /// let arr = PyArray3::<i32>::new(py, [4, 5, 6], false);
393
+ ///
394
+ /// for i in 0..4 {
395
+ /// for j in 0..5 {
396
+ /// for k in 0..6 {
397
+ /// arr.uget_raw([i, j, k]).write((i * j * k) as i32);
398
+ /// }
399
+ /// }
400
+ /// }
401
+ ///
402
+ /// arr
403
+ /// };
404
+ ///
379
405
/// assert_eq!(arr.shape(), &[4, 5, 6]);
380
406
/// });
381
407
/// ```
382
- pub fn new < ID > ( py : Python , dims : ID , is_fortran : bool ) -> & Self
408
+ pub unsafe fn new < ID > ( py : Python , dims : ID , is_fortran : bool ) -> & Self
383
409
where
384
410
ID : IntoDimension < Dim = D > ,
385
411
{
386
412
let flags = if is_fortran { 1 } else { 0 } ;
387
- unsafe { PyArray :: new_ ( py, dims, ptr:: null_mut ( ) , flags) }
413
+ PyArray :: new_ ( py, dims, ptr:: null_mut ( ) , flags)
388
414
}
389
415
390
416
pub ( crate ) unsafe fn new_ < ID > (
@@ -447,6 +473,9 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
447
473
/// If `is_fortran` is true, then
448
474
/// a fortran order array is created, otherwise a C-order array is created.
449
475
///
476
+ /// For elements with `DATA_TYPE == DataType::Object`, this will fill the array
477
+ /// with valid pointers to zero-valued Python integer objects.
478
+ ///
450
479
/// See also [PyArray_Zeros](https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Zeros)
451
480
///
452
481
/// # Example
@@ -593,6 +622,16 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
593
622
& mut * ( self . data ( ) . offset ( offset) as * mut _ )
594
623
}
595
624
625
+ /// Same as [uget](#method.uget), but returns `*mut T`.
626
+ #[ inline( always) ]
627
+ pub unsafe fn uget_raw < Idx > ( & self , index : Idx ) -> * mut T
628
+ where
629
+ Idx : NpyIndex < Dim = D > ,
630
+ {
631
+ let offset = index. get_unchecked :: < T > ( self . strides ( ) ) ;
632
+ self . data ( ) . offset ( offset) as * mut _
633
+ }
634
+
596
635
/// Get dynamic dimensioned array from fixed dimension array.
597
636
pub fn to_dyn ( & self ) -> & PyArray < T , IxDyn > {
598
637
let python = self . py ( ) ;
@@ -730,20 +769,18 @@ impl<T: Element> PyArray<T, Ix1> {
730
769
/// });
731
770
/// ```
732
771
pub fn from_slice < ' py > ( py : Python < ' py > , slice : & [ T ] ) -> & ' py Self {
733
- let array = PyArray :: new ( py , [ slice . len ( ) ] , false ) ;
734
- if T :: DATA_TYPE != DataType :: Object {
735
- unsafe {
772
+ unsafe {
773
+ let array = PyArray :: new ( py , [ slice . len ( ) ] , false ) ;
774
+ if T :: DATA_TYPE != DataType :: Object {
736
775
array. copy_ptr ( slice. as_ptr ( ) , slice. len ( ) ) ;
737
- }
738
- } else {
739
- unsafe {
776
+ } else {
740
777
let data_ptr = array. data ( ) ;
741
778
for ( i, item) in slice. iter ( ) . enumerate ( ) {
742
779
data_ptr. add ( i) . write ( item. clone ( ) ) ;
743
780
}
744
781
}
782
+ array
745
783
}
746
- array
747
784
}
748
785
749
786
/// Construct one-dimension PyArray
@@ -776,20 +813,15 @@ impl<T: Element> PyArray<T, Ix1> {
776
813
/// });
777
814
/// ```
778
815
pub fn from_exact_iter ( py : Python < ' _ > , iter : impl ExactSizeIterator < Item = T > ) -> & Self {
779
- // Use zero-initialized pointers for object arrays
780
- // so that partially initialized arrays can be dropped safely
781
- // in case the iterator implementation panics.
782
- let array = if T :: DATA_TYPE == DataType :: Object {
783
- Self :: zeros ( py, [ iter. len ( ) ] , false )
784
- } else {
785
- Self :: new ( py, [ iter. len ( ) ] , false )
786
- } ;
816
+ // NumPy will always zero-initialize object pointers,
817
+ // so the array can be dropped safely if the iterator panics.
787
818
unsafe {
819
+ let array = Self :: new ( py, [ iter. len ( ) ] , false ) ;
788
820
for ( i, item) in iter. enumerate ( ) {
789
- * array. uget_mut ( [ i] ) = item;
821
+ array. uget_raw ( [ i] ) . write ( item) ;
790
822
}
823
+ array
791
824
}
792
- array
793
825
}
794
826
795
827
/// Construct one-dimension PyArray from a type which implements
@@ -811,16 +843,11 @@ impl<T: Element> PyArray<T, Ix1> {
811
843
let iter = iter. into_iter ( ) ;
812
844
let ( min_len, max_len) = iter. size_hint ( ) ;
813
845
let mut capacity = max_len. unwrap_or_else ( || min_len. max ( 512 / mem:: size_of :: < T > ( ) ) ) ;
814
- // Use zero-initialized pointers for object arrays
815
- // so that partially initialized arrays can be dropped safely
816
- // in case the iterator implementation panics.
817
- let array = if T :: DATA_TYPE == DataType :: Object {
818
- Self :: zeros ( py, [ capacity] , false )
819
- } else {
820
- Self :: new ( py, [ capacity] , false )
821
- } ;
822
- let mut length = 0 ;
823
846
unsafe {
847
+ // NumPy will always zero-initialize object pointers,
848
+ // so the array can be dropped safely if the iterator panics.
849
+ let array = Self :: new ( py, [ capacity] , false ) ;
850
+ let mut length = 0 ;
824
851
for ( i, item) in iter. enumerate ( ) {
825
852
length += 1 ;
826
853
if length > capacity {
@@ -829,13 +856,13 @@ impl<T: Element> PyArray<T, Ix1> {
829
856
. resize ( capacity)
830
857
. expect ( "PyArray::from_iter: Failed to allocate memory" ) ;
831
858
}
832
- * array. uget_mut ( [ i] ) = item;
859
+ array. uget_raw ( [ i] ) . write ( item) ;
833
860
}
861
+ if capacity > length {
862
+ array. resize ( length) . unwrap ( )
863
+ }
864
+ array
834
865
}
835
- if capacity > length {
836
- array. resize ( length) . unwrap ( )
837
- }
838
- array
839
866
}
840
867
841
868
/// Extends or trancates the length of 1 dimension PyArray.
@@ -909,15 +936,15 @@ impl<T: Element> PyArray<T, Ix2> {
909
936
return Err ( FromVecError :: new ( v. len ( ) , last_len) ) ;
910
937
}
911
938
let dims = [ v. len ( ) , last_len] ;
912
- let array = Self :: new ( py, dims, false ) ;
913
939
unsafe {
940
+ let array = Self :: new ( py, dims, false ) ;
914
941
for ( y, vy) in v. iter ( ) . enumerate ( ) {
915
942
for ( x, vyx) in vy. iter ( ) . enumerate ( ) {
916
- * array. uget_mut ( [ y, x] ) = vyx. clone ( ) ;
943
+ array. uget_raw ( [ y, x] ) . write ( vyx. clone ( ) ) ;
917
944
}
918
945
}
946
+ Ok ( array)
919
947
}
920
- Ok ( array)
921
948
}
922
949
}
923
950
@@ -951,17 +978,17 @@ impl<T: Element> PyArray<T, Ix3> {
951
978
return Err ( FromVecError :: new ( v. len ( ) , len3) ) ;
952
979
}
953
980
let dims = [ v. len ( ) , len2, len3] ;
954
- let array = Self :: new ( py, dims, false ) ;
955
981
unsafe {
982
+ let array = Self :: new ( py, dims, false ) ;
956
983
for ( z, vz) in v. iter ( ) . enumerate ( ) {
957
984
for ( y, vzy) in vz. iter ( ) . enumerate ( ) {
958
985
for ( x, vzyx) in vzy. iter ( ) . enumerate ( ) {
959
- * array. uget_mut ( [ z, y, x] ) = vzyx. clone ( ) ;
986
+ array. uget_raw ( [ z, y, x] ) . write ( vzyx. clone ( ) ) ;
960
987
}
961
988
}
962
989
}
990
+ Ok ( array)
963
991
}
964
- Ok ( array)
965
992
}
966
993
}
967
994
@@ -972,7 +999,7 @@ impl<T: Element, D> PyArray<T, D> {
972
999
/// use numpy::PyArray;
973
1000
/// pyo3::Python::with_gil(|py| {
974
1001
/// let pyarray_f = PyArray::arange(py, 2.0, 5.0, 1.0);
975
- /// let pyarray_i = PyArray::<i64, _>::new(py, [3], false);
1002
+ /// let pyarray_i = unsafe { PyArray::<i64, _>::new(py, [3], false) } ;
976
1003
/// assert!(pyarray_f.copy_to(pyarray_i).is_ok());
977
1004
/// assert_eq!(pyarray_i.readonly().as_slice().unwrap(), &[2, 3, 4]);
978
1005
/// });
0 commit comments