vortex_scalar/
null.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Null scalar conversion implementations.
5//!
6//! This module provides conversions between Scalar values and the unit type `()`.
7//!
8//! # Conversion Behavior
9//!
10//! The `TryFrom<&Scalar>` implementation for `()` succeeds in two cases:
11//!
12//! 1. **Pure null scalars**: Scalars with `DType::Null` always convert successfully to `()`.
13//!
14//! 2. **Typed null scalars**: Scalars of any type (primitive, string, binary, etc.) that
15//!    have a null value also convert successfully to `()`. This includes scalars created
16//!    with methods like `Scalar::null_typed::<T>()`.
17//!
18//! This behavior means that typed null scalars (e.g., a null i32 or null string) are
19//! treated equivalently to pure null scalars for the purpose of unit type conversion.
20//!
21//! # Examples
22//!
23//! ```ignore
24//! use vortex_scalar::Scalar;
25//! use vortex_dtype::DType;
26//!
27//! // Pure null scalar converts to ()
28//! let null_scalar = Scalar::null(DType::Null);
29//! let unit: () = null_scalar.try_into().unwrap();
30//!
31//! // Typed null scalar also converts to ()
32//! let null_int = Scalar::null_typed::<i32>();
33//! let unit: () = null_int.try_into().unwrap();
34//!
35//! // Non-null scalar fails conversion
36//! let int_scalar = Scalar::primitive(42i32, Nullability::NonNullable);
37//! let result = <()>::try_from(&int_scalar);
38//! assert!(result.is_err());
39//! ```
40
41use vortex_error::VortexError;
42
43use crate::Scalar;
44
45impl TryFrom<&Scalar> for () {
46    type Error = VortexError;
47
48    fn try_from(scalar: &Scalar) -> Result<Self, Self::Error> {
49        scalar.value().as_null()
50    }
51}
52
53impl TryFrom<Scalar> for () {
54    type Error = VortexError;
55
56    fn try_from(scalar: Scalar) -> Result<Self, Self::Error> {
57        <()>::try_from(&scalar)
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use vortex_dtype::{DType, Nullability};
64
65    use super::*;
66
67    #[test]
68    fn test_null_scalar_try_from_ref() {
69        let null_scalar = Scalar::null(DType::Null);
70
71        let result = <()>::try_from(&null_scalar);
72        assert!(result.is_ok());
73    }
74
75    #[test]
76    fn test_null_scalar_try_from_owned() {
77        let null_scalar = Scalar::null(DType::Null);
78
79        let result = <()>::try_from(null_scalar);
80        assert!(result.is_ok());
81    }
82
83    #[test]
84    fn test_non_null_scalar_fails_ref() {
85        let int_scalar = Scalar::primitive(42i32, Nullability::NonNullable);
86
87        let result = <()>::try_from(&int_scalar);
88        assert!(result.is_err());
89    }
90
91    #[test]
92    fn test_non_null_scalar_fails_owned() {
93        let int_scalar = Scalar::primitive(42i32, Nullability::NonNullable);
94
95        let result = <()>::try_from(int_scalar);
96        assert!(result.is_err());
97    }
98
99    #[test]
100    fn test_nullable_primitive_with_null_value() {
101        let null_int = Scalar::null_typed::<i32>();
102
103        // NOTE: Unexpected behavior - TryFrom succeeds for typed null scalars
104        let result = <()>::try_from(&null_int);
105        assert!(result.is_ok());
106    }
107
108    #[test]
109    fn test_null_string() {
110        let null_string = Scalar::null_typed::<String>();
111
112        // NOTE: Unexpected behavior - TryFrom succeeds for typed null scalars
113        let result = <()>::try_from(&null_string);
114        assert!(result.is_ok());
115    }
116
117    #[test]
118    fn test_null_bool() {
119        let null_bool = Scalar::null_typed::<bool>();
120
121        // NOTE: Unexpected behavior - TryFrom succeeds for typed null scalars
122        let result = <()>::try_from(&null_bool);
123        assert!(result.is_ok());
124    }
125
126    #[test]
127    fn test_null_struct() {
128        use vortex_dtype::{FieldDType, StructFields};
129
130        let struct_dtype = DType::Struct(
131            StructFields::from_iter([("field1", FieldDType::from(DType::Null))]),
132            Nullability::Nullable,
133        );
134
135        let null_struct = Scalar::struct_(struct_dtype, vec![Scalar::null(DType::Null)]);
136
137        // This should fail because it's a struct, not a pure null type
138        let result = <()>::try_from(&null_struct);
139        assert!(result.is_err());
140    }
141
142    #[test]
143    fn test_null_binary() {
144        let null_binary = Scalar::null(DType::Binary(Nullability::Nullable));
145
146        // NOTE: Unexpected behavior - TryFrom succeeds for typed null scalars
147        let result = <()>::try_from(&null_binary);
148        assert!(result.is_ok());
149    }
150}