You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PyArray1::<bool>::as_slice returns a &[bool], but the underlying data may not at all be valid for Rust's bool, creating instant undefined behavior. For example
Thanks for reaching out. This looks unfortunate. I do wonder however how sound that Python implementation is in itself. Our bool array implementation is based on NPY_BOOL
In its documentation it's pretty clearly mentioned that its byte values may only ever by 0 or 1, just like Rust's bool. Under this assumption I believe our implementation to be sound. And if we start with a boolean array this invariant will also be upheld:
So view in some sense should be considered unsafe because it can be used to violate this assumption.
There are other functions that are problematic in this sense, for example numpy.empty. Accessing uninitialized memory without MaybeUninit is also unsound even if the type can hold any bit pattern. There is an implicit safety contract that the Python code is assumed to be well behaved, i.e. not hand us uninitialized memory or break type invariant. I don't think there is a practical way for us to guard against this (but I'm happy to hear otherwise)
It is unfortunate that np.view doesn't document what kind of views are 'allowed' and which would be undefined behavior. Then again, np.empty is essentially instant undefined behavior anywhere so there's a certain lack of care from the numpy project in this regardless.
I'm perfectly happy to chalk this up to np.view having an implicit unsafe bound. That said, I'll keep the the workaround I used in Polars so that we match numpy's behavior of 0 = false, 1+ = true, regardless of what the documentation says.
@ngoldbaum The view isn't called from Rust code, that happens in Python. The Rust code just gets an ndarray and has no idea if it's in a valid state or not.
Numpy could opt to add more checks to support Rust's safety needs, but that might break uses that don't use Rust or obey Rust's rules that rely on that unsafety.
It may be the case that rust-numpy can't safely assume the things it does about the sanity of data in numpy arrays and add runtime checks...
Activity
Icxolu commentedon Sep 1, 2025
Thanks for reaching out. This looks unfortunate. I do wonder however how sound that Python implementation is in itself. Our
boolarray implementation is based onNPY_BOOLrust-numpy/src/dtype.rs
Line 599 in 283f122
In its documentation it's pretty clearly mentioned that its byte values may only ever by 0 or 1, just like Rust's
bool. Under this assumption I believe our implementation to be sound. And if we start with a boolean array this invariant will also be upheld:So
viewin some sense should be consideredunsafebecause it can be used to violate this assumption.There are other functions that are problematic in this sense, for example
numpy.empty. Accessing uninitialized memory withoutMaybeUninitis also unsound even if the type can hold any bit pattern. There is an implicit safety contract that the Python code is assumed to be well behaved, i.e. not hand us uninitialized memory or break type invariant. I don't think there is a practical way for us to guard against this (but I'm happy to hear otherwise)orlp commentedon Sep 1, 2025
It is unfortunate that
np.viewdoesn't document what kind of views are 'allowed' and which would be undefined behavior. Then again,np.emptyis essentially instant undefined behavior anywhere so there's a certain lack of care from the numpy project in this regardless.I'm perfectly happy to chalk this up to
np.viewhaving an implicitunsafebound. That said, I'll keep the the workaround I used in Polars so that we match numpy's behavior of0 = false, 1+ = true, regardless of what the documentation says.ngoldbaum commentedon Sep 2, 2025
IMO rust-numpy should add checks like what OP proposes to the bindings for
viewandempty. Or mark them as unsafe.orlp commentedon Sep 2, 2025
@ngoldbaum The
viewisn't called from Rust code, that happens in Python. The Rust code just gets anndarrayand has no idea if it's in a valid state or not.ngoldbaum commentedon Sep 2, 2025
Ah, fair enough. Seems like another issue similar to what Alex Gaynor describes regarding the buffer protocol: https://alexgaynor.net/2022/oct/23/buffers-on-the-edge/
Numpy could opt to add more checks to support Rust's safety needs, but that might break uses that don't use Rust or obey Rust's rules that rely on that unsafety.
It may be the case that rust-numpy can't safely assume the things it does about the sanity of data in numpy arrays and add runtime checks...