This is an automated email from the ASF dual-hosted git repository.

Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 0408eef441 Bump MSRV to 1.88 (#10254)
0408eef441 is described below

commit 0408eef441c7754ddc6e7473c0d02aa6ee076bac
Author: Andrew Lamb <[email protected]>
AuthorDate: Mon Aug 3 20:21:19 2026 -0400

    Bump MSRV to 1.88 (#10254)
    
    # Which issue does this PR close?
    
    - Part of #10226.
    
    # Rationale for this change
    
    This implements the suggestion from
    https://github.com/apache/arrow-rs/issues/10226#issuecomment-4860000468
    to bump the Minimum Supported Rust Version (MSRV) from Rust 1.85 to Rust
    1.88 for the next major release.
    
    # What changes are included in this PR?
    
    - Updates the workspace MSRV to 1.88.
    - Updates the standalone pyarrow testing crate MSRV pins to 1.88.
    - Removes now-unnecessary `std::mem::transmute` workarounds in
    `arrow-array` that were only needed because const `f32::from_bits` and
    `f64::from_bits` were unavailable under the previous MSRV.
    
    # Are there any user-facing changes?
    
    Users building arrow-rs crates will need Rust 1.88 or newer.
    
    # How was this tested?
    CI
    
    ---------
    
    Co-authored-by: Jeffrey Vo <[email protected]>
---
 Cargo.toml                                         |  2 +-
 arrow-array/src/arithmetic.rs                      | 29 ++------
 arrow-array/src/array/byte_array.rs                | 18 ++---
 arrow-array/src/array/byte_view_array.rs           | 18 ++---
 arrow-array/src/array/fixed_size_binary_array.rs   | 41 ++++++++---
 arrow-array/src/array/fixed_size_list_array.rs     | 32 ++++-----
 arrow-array/src/array/list_array.rs                | 16 ++---
 arrow-array/src/array/list_view_array.rs           | 16 ++---
 arrow-array/src/array/map_array.rs                 | 14 ++--
 arrow-array/src/array/primitive_array.rs           | 16 ++---
 arrow-array/src/array/struct_array.rs              | 34 +++++----
 .../src/builder/generic_bytes_view_builder.rs      | 54 +++++++-------
 arrow-avro/src/codec.rs                            | 56 +++++++--------
 arrow-avro/src/reader/mod.rs                       | 57 ++++++++-------
 arrow-avro/src/reader/record.rs                    | 49 ++++++-------
 arrow-avro/src/schema.rs                           |  6 +-
 arrow-buffer/src/buffer/boolean.rs                 |  2 +-
 arrow-buffer/src/buffer/immutable.rs               | 26 +++----
 arrow-csv/src/reader/mod.rs                        |  8 +--
 arrow-data/src/data.rs                             |  8 +--
 arrow-data/src/equal/boolean.rs                    |  8 +--
 arrow-flight/src/decode.rs                         |  3 +-
 arrow-flight/src/sql/client.rs                     |  5 +-
 arrow-flight/tests/common/trailers_layer.rs        |  8 +--
 arrow-ipc/src/convert.rs                           | 43 ++++++------
 arrow-ipc/src/writer.rs                            |  2 +-
 arrow-json/src/reader/value_iter.rs                |  8 +--
 arrow-json/src/writer/encoder.rs                   |  8 +--
 arrow-pyarrow-integration-testing/Cargo.toml       |  2 +-
 arrow-pyarrow-testing/Cargo.toml                   |  2 +-
 arrow-row/src/lib.rs                               | 12 ++--
 arrow-schema/src/schema.rs                         | 14 ++--
 arrow-select/src/coalesce.rs                       | 44 ++++++------
 arrow-select/src/dictionary.rs                     |  4 +-
 arrow-string/src/concat_elements.rs                | 16 ++---
 arrow/src/tensor.rs                                | 24 +++----
 parquet-variant-compute/src/variant_array.rs       |  8 +--
 parquet-variant-compute/src/variant_get.rs         | 38 +++++-----
 parquet-variant/src/variant.rs                     |  4 +-
 parquet-variant/src/variant/metadata.rs            | 12 ++--
 parquet-variant/src/variant/object.rs              | 12 ++--
 parquet/benches/row_selector.rs                    |  2 +-
 .../src/arrow/array_reader/fixed_len_byte_array.rs | 16 ++---
 .../arrow/array_reader/fixed_size_list_array.rs    |  8 +--
 parquet/src/arrow/arrow_reader/read_plan.rs        | 10 +--
 parquet/src/arrow/arrow_writer/levels.rs           | 11 +--
 parquet/src/arrow/arrow_writer/mod.rs              | 66 ++++++++---------
 parquet/src/arrow/async_reader/store.rs            | 15 ++--
 parquet/src/arrow/buffer/offset_buffer.rs          | 16 ++---
 .../src/arrow/push_decoder/reader_builder/mod.rs   |  8 +--
 .../src/arrow/record_reader/definition_levels.rs   | 35 +++++----
 parquet/src/column/chunker/cdc.rs                  | 12 ++--
 parquet/src/column/reader.rs                       | 12 ++--
 parquet/src/encodings/decoding.rs                  |  6 +-
 .../decoding/byte_stream_split_decoder.rs          |  2 +-
 parquet/src/file/metadata/reader.rs                |  8 +--
 parquet/src/file/metadata/thrift/mod.rs            | 27 ++++---
 parquet/src/file/page_index/column_index.rs        | 82 ++++++++++++----------
 parquet/src/file/properties.rs                     |  8 +--
 parquet/src/file/serialized_reader.rs              |  8 +--
 parquet/src/file/statistics.rs                     | 24 +++----
 parquet/src/file/writer.rs                         |  8 +--
 parquet/src/schema/types.rs                        | 15 ++--
 parquet/src/util/bit_util.rs                       |  2 +-
 parquet_derive/src/parquet_field.rs                | 43 ++++++------
 65 files changed, 604 insertions(+), 619 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 4274307c69..28acc4125f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -82,7 +82,7 @@ include = [
     "NOTICE.txt",
 ]
 edition = "2024"
-rust-version = "1.85"
+rust-version = "1.88"
 
 [workspace.dependencies]
 arrow = { version = "59.2.0", path = "./arrow", default-features = false }
diff --git a/arrow-array/src/arithmetic.rs b/arrow-array/src/arithmetic.rs
index 52708da781..08f0b968ab 100644
--- a/arrow-array/src/arithmetic.rs
+++ b/arrow-array/src/arithmetic.rs
@@ -413,40 +413,19 @@ native_type_float_op!(
     f16::from_bits(-1 as _),
     f16::from_bits(i16::MAX as _)
 );
-// from_bits is not yet stable as const fn, see 
https://github.com/rust-lang/rust/issues/72447
 native_type_float_op!(
     f32,
     0.,
     1.,
-    unsafe {
-        // Need to allow in clippy because
-        // current MSRV (Minimum Supported Rust Version) is `1.85.0` but this 
item is stable since `1.87.0`
-        #[allow(unnecessary_transmutes)]
-        std::mem::transmute(-1_i32)
-    },
-    unsafe {
-        // Need to allow in clippy because
-        // current MSRV (Minimum Supported Rust Version) is `1.85.0` but this 
item is stable since `1.87.0`
-        #[allow(unnecessary_transmutes)]
-        std::mem::transmute(i32::MAX)
-    }
+    f32::from_bits(-1_i32 as _),
+    f32::from_bits(i32::MAX as _)
 );
 native_type_float_op!(
     f64,
     0.,
     1.,
-    unsafe {
-        // Need to allow in clippy because
-        // current MSRV (Minimum Supported Rust Version) is `1.85.0` but this 
item is stable since `1.87.0`
-        #[allow(unnecessary_transmutes)]
-        std::mem::transmute(-1_i64)
-    },
-    unsafe {
-        // Need to allow in clippy because
-        // current MSRV (Minimum Supported Rust Version) is `1.85.0` but this 
item is stable since `1.87.0`
-        #[allow(unnecessary_transmutes)]
-        std::mem::transmute(i64::MAX)
-    }
+    f64::from_bits(-1_i64 as _),
+    f64::from_bits(i64::MAX as _)
 );
 
 #[cfg(test)]
diff --git a/arrow-array/src/array/byte_array.rs 
b/arrow-array/src/array/byte_array.rs
index 93924ac76b..e582968174 100644
--- a/arrow-array/src/array/byte_array.rs
+++ b/arrow-array/src/array/byte_array.rs
@@ -135,15 +135,15 @@ impl<T: ByteArrayType> GenericByteArray<T> {
         // Verify that each pair of offsets is a valid slices of values
         T::validate(&offsets, &values)?;
 
-        if let Some(n) = nulls.as_ref() {
-            if n.len() != len {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect length of null buffer for {}{}Array, expected 
{len} got {}",
-                    T::Offset::PREFIX,
-                    T::PREFIX,
-                    n.len(),
-                )));
-            }
+        if let Some(n) = nulls.as_ref()
+            && n.len() != len
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect length of null buffer for {}{}Array, expected {len} 
got {}",
+                T::Offset::PREFIX,
+                T::PREFIX,
+                n.len(),
+            )));
         }
 
         Ok(Self {
diff --git a/arrow-array/src/array/byte_view_array.rs 
b/arrow-array/src/array/byte_view_array.rs
index 58b5ce7a05..32dde2f593 100644
--- a/arrow-array/src/array/byte_view_array.rs
+++ b/arrow-array/src/array/byte_view_array.rs
@@ -213,15 +213,15 @@ impl<T: ByteViewType + ?Sized> GenericByteViewArray<T> {
 
         T::validate(&views, &buffers)?;
 
-        if let Some(n) = nulls.as_ref() {
-            if n.len() != views.len() {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect length of null buffer for {}ViewArray, expected 
{} got {}",
-                    T::PREFIX,
-                    views.len(),
-                    n.len(),
-                )));
-            }
+        if let Some(n) = nulls.as_ref()
+            && n.len() != views.len()
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect length of null buffer for {}ViewArray, expected {} 
got {}",
+                T::PREFIX,
+                views.len(),
+                n.len(),
+            )));
         }
 
         Ok(Self {
diff --git a/arrow-array/src/array/fixed_size_binary_array.rs 
b/arrow-array/src/array/fixed_size_binary_array.rs
index b53c001a89..98a812460b 100644
--- a/arrow-array/src/array/fixed_size_binary_array.rs
+++ b/arrow-array/src/array/fixed_size_binary_array.rs
@@ -173,8 +173,29 @@ impl FixedSizeBinaryArray {
         })?;
 
         let len = match values.len().checked_div(value_size) {
-            Some(len) => len,
-            None => nulls.as_ref().map(|n| n.len()).unwrap_or(0),
+            Some(len) => {
+                if let Some(n) = nulls.as_ref()
+                    && n.len() != len
+                {
+                    return Err(ArrowError::InvalidArgumentError(format!(
+                        "Incorrect length of null buffer for 
FixedSizeBinaryArray, expected {} got {}",
+                        len,
+                        n.len(),
+                    )));
+                }
+
+                len
+            }
+            None => {
+                if !values.is_empty() {
+                    return Err(ArrowError::InvalidArgumentError(
+                        "Buffer cannot have non-zero length if the value 
length is zero".to_owned(),
+                    ));
+                }
+
+                // If the value length is zero, try to determine the length 
from the null buffer
+                nulls.as_ref().map(|n| n.len()).unwrap_or(0)
+            }
         };
 
         Self::try_new_with_len(value_length, values, nulls, len)
@@ -204,14 +225,14 @@ impl FixedSizeBinaryArray {
             ))
         })?;
 
-        if let Some(nulls) = &nulls {
-            if nulls.len() != len {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect length of null buffer for FixedSizeBinaryArray, 
expected {} got {}",
-                    len,
-                    nulls.len(),
-                )));
-            }
+        if let Some(nulls) = &nulls
+            && nulls.len() != len
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect length of null buffer for FixedSizeBinaryArray, 
expected {} got {}",
+                len,
+                nulls.len(),
+            )));
         }
 
         if value_size != 0 && values.len() / value_size != len {
diff --git a/arrow-array/src/array/fixed_size_list_array.rs 
b/arrow-array/src/array/fixed_size_list_array.rs
index 797fedbc71..a6255482be 100644
--- a/arrow-array/src/array/fixed_size_list_array.rs
+++ b/arrow-array/src/array/fixed_size_list_array.rs
@@ -201,7 +201,7 @@ impl FixedSizeListArray {
 
             Self::try_new_with_length(field, size, values, nulls, len)
         } else {
-            if values.len() % s != 0 {
+            if !values.len().is_multiple_of(s) {
                 return Err(ArrowError::InvalidArgumentError(format!(
                     "Incorrect length of values buffer for FixedSizeListArray, 
\
                      expected a multiple of {s} got {}",
@@ -212,15 +212,15 @@ impl FixedSizeListArray {
             let len = values.len() / s;
 
             // Check that the null buffer length is correct (if it exists).
-            if let Some(null_buffer) = &nulls {
-                if s * null_buffer.len() != values.len() {
-                    return Err(ArrowError::InvalidArgumentError(format!(
-                        "Incorrect length of values buffer for 
FixedSizeListArray, \
+            if let Some(null_buffer) = &nulls
+                && s * null_buffer.len() != values.len()
+            {
+                return Err(ArrowError::InvalidArgumentError(format!(
+                    "Incorrect length of values buffer for FixedSizeListArray, 
\
                             expected {} got {}",
-                        s * null_buffer.len(),
-                        values.len(),
-                    )));
-                }
+                    s * null_buffer.len(),
+                    values.len(),
+                )));
             }
 
             Self::try_new_with_length(field, size, values, nulls, len)
@@ -253,13 +253,13 @@ impl FixedSizeListArray {
             ArrowError::InvalidArgumentError(format!("Size cannot be negative, 
got {size}"))
         })?;
 
-        if let Some(null_buffer) = &nulls {
-            if null_buffer.len() != len {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Invalid null buffer for FixedSizeListArray, expected 
{len} found {}",
-                    null_buffer.len()
-                )));
-            }
+        if let Some(null_buffer) = &nulls
+            && null_buffer.len() != len
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Invalid null buffer for FixedSizeListArray, expected {len} 
found {}",
+                null_buffer.len()
+            )));
         }
 
         if s == 0 && !values.is_empty() {
diff --git a/arrow-array/src/array/list_array.rs 
b/arrow-array/src/array/list_array.rs
index 4ae2b5668c..69c642b977 100644
--- a/arrow-array/src/array/list_array.rs
+++ b/arrow-array/src/array/list_array.rs
@@ -223,14 +223,14 @@ impl<OffsetSize: OffsetSizeTrait> 
GenericListArray<OffsetSize> {
             )));
         }
 
-        if let Some(n) = nulls.as_ref() {
-            if n.len() != len {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect length of null buffer for {}ListArray, expected 
{len} got {}",
-                    OffsetSize::PREFIX,
-                    n.len(),
-                )));
-            }
+        if let Some(n) = nulls.as_ref()
+            && n.len() != len
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect length of null buffer for {}ListArray, expected 
{len} got {}",
+                OffsetSize::PREFIX,
+                n.len(),
+            )));
         }
         if !field.is_nullable() && values.is_nullable() {
             return Err(ArrowError::InvalidArgumentError(format!(
diff --git a/arrow-array/src/array/list_view_array.rs 
b/arrow-array/src/array/list_view_array.rs
index a032d8715a..8e7765be70 100644
--- a/arrow-array/src/array/list_view_array.rs
+++ b/arrow-array/src/array/list_view_array.rs
@@ -146,14 +146,14 @@ impl<OffsetSize: OffsetSizeTrait> 
GenericListViewArray<OffsetSize> {
         nulls: Option<NullBuffer>,
     ) -> Result<Self, ArrowError> {
         let len = offsets.len();
-        if let Some(n) = nulls.as_ref() {
-            if n.len() != len {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect length of null buffer for {}ListViewArray, 
expected {len} got {}",
-                    OffsetSize::PREFIX,
-                    n.len(),
-                )));
-            }
+        if let Some(n) = nulls.as_ref()
+            && n.len() != len
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect length of null buffer for {}ListViewArray, expected 
{len} got {}",
+                OffsetSize::PREFIX,
+                n.len(),
+            )));
         }
         if len != sizes.len() {
             return Err(ArrowError::InvalidArgumentError(format!(
diff --git a/arrow-array/src/array/map_array.rs 
b/arrow-array/src/array/map_array.rs
index 3d7648120b..2e4d054ec6 100644
--- a/arrow-array/src/array/map_array.rs
+++ b/arrow-array/src/array/map_array.rs
@@ -80,13 +80,13 @@ impl MapArray {
             )));
         }
 
-        if let Some(n) = nulls.as_ref() {
-            if n.len() != len {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect length of null buffer for MapArray, expected 
{len} got {}",
-                    n.len(),
-                )));
-            }
+        if let Some(n) = nulls.as_ref()
+            && n.len() != len
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect length of null buffer for MapArray, expected {len} 
got {}",
+                n.len(),
+            )));
         }
         if field.is_nullable() || entries.null_count() != 0 {
             return Err(ArrowError::InvalidArgumentError(
diff --git a/arrow-array/src/array/primitive_array.rs 
b/arrow-array/src/array/primitive_array.rs
index 8946af2b74..321d2cb27a 100644
--- a/arrow-array/src/array/primitive_array.rs
+++ b/arrow-array/src/array/primitive_array.rs
@@ -673,14 +673,14 @@ impl<T: ArrowPrimitiveType> PrimitiveArray<T> {
         values: ScalarBuffer<T::Native>,
         nulls: Option<NullBuffer>,
     ) -> Result<Self, ArrowError> {
-        if let Some(n) = nulls.as_ref() {
-            if n.len() != values.len() {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect length of null buffer for PrimitiveArray, 
expected {} got {}",
-                    values.len(),
-                    n.len(),
-                )));
-            }
+        if let Some(n) = nulls.as_ref()
+            && n.len() != values.len()
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect length of null buffer for PrimitiveArray, expected 
{} got {}",
+                values.len(),
+                n.len(),
+            )));
         }
 
         Ok(Self {
diff --git a/arrow-array/src/array/struct_array.rs 
b/arrow-array/src/array/struct_array.rs
index 59513b9ff0..bf7dc934dc 100644
--- a/arrow-array/src/array/struct_array.rs
+++ b/arrow-array/src/array/struct_array.rs
@@ -138,13 +138,13 @@ impl StructArray {
             )));
         }
 
-        if let Some(n) = nulls.as_ref() {
-            if n.len() != len {
-                return Err(ArrowError::InvalidArgumentError(format!(
-                    "Incorrect number of nulls for StructArray, expected {len} 
got {}",
-                    n.len(),
-                )));
-            }
+        if let Some(n) = nulls.as_ref()
+            && n.len() != len
+        {
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Incorrect number of nulls for StructArray, expected {len} got 
{}",
+                n.len(),
+            )));
         }
 
         for (f, a) in fields.iter().zip(&arrays) {
@@ -166,17 +166,15 @@ impl StructArray {
                 )));
             }
 
-            if !f.is_nullable() {
-                if let Some(a) = a.logical_nulls() {
-                    if !nulls.as_ref().map(|n| 
n.contains(&a)).unwrap_or_default()
-                        && a.null_count() > 0
-                    {
-                        return Err(ArrowError::InvalidArgumentError(format!(
-                            "Found unmasked nulls for non-nullable StructArray 
field {:?}",
-                            f.name()
-                        )));
-                    }
-                }
+            if !f.is_nullable()
+                && let Some(a) = a.logical_nulls()
+                && !nulls.as_ref().map(|n| n.contains(&a)).unwrap_or_default()
+                && a.null_count() > 0
+            {
+                return Err(ArrowError::InvalidArgumentError(format!(
+                    "Found unmasked nulls for non-nullable StructArray field 
{:?}",
+                    f.name()
+                )));
             }
         }
 
diff --git a/arrow-array/src/builder/generic_bytes_view_builder.rs 
b/arrow-array/src/builder/generic_bytes_view_builder.rs
index 2d60187344..4886d56e7f 100644
--- a/arrow-array/src/builder/generic_bytes_view_builder.rs
+++ b/arrow-array/src/builder/generic_bytes_view_builder.rs
@@ -359,36 +359,34 @@ impl<T: ByteViewType + ?Sized> GenericByteViewBuilder<T> {
                 .max_deduplication_len
                 .map(|max_length| length <= max_length)
                 .unwrap_or(true);
-        if can_deduplicate {
-            if let Some((mut ht, hasher)) = self.string_tracker.take() {
-                let hash_val = hasher.hash_one(v);
-                let hasher_fn = |v: &_| hasher.hash_one(v);
-
-                let entry = ht.entry(
-                    hash_val,
-                    |idx| {
-                        let stored_value = self.get_value(*idx);
-                        v == stored_value
-                    },
-                    hasher_fn,
-                );
-                match entry {
-                    Entry::Occupied(occupied) => {
-                        // If the string already exists, we will directly use 
the view
-                        let idx = occupied.get();
-                        self.views_buffer.push(self.views_buffer[*idx]);
-                        self.null_buffer_builder.append_non_null();
-                        self.string_tracker = Some((ht, hasher));
-                        return Ok(());
-                    }
-                    Entry::Vacant(vacant) => {
-                        // o.w. we insert the (string hash -> view index)
-                        // the idx is current length of views_builder, as we 
are inserting a new view
-                        vacant.insert(self.views_buffer.len());
-                    }
+        if can_deduplicate && let Some((mut ht, hasher)) = 
self.string_tracker.take() {
+            let hash_val = hasher.hash_one(v);
+            let hasher_fn = |v: &_| hasher.hash_one(v);
+
+            let entry = ht.entry(
+                hash_val,
+                |idx| {
+                    let stored_value = self.get_value(*idx);
+                    v == stored_value
+                },
+                hasher_fn,
+            );
+            match entry {
+                Entry::Occupied(occupied) => {
+                    // If the string already exists, we will directly use the 
view
+                    let idx = occupied.get();
+                    self.views_buffer.push(self.views_buffer[*idx]);
+                    self.null_buffer_builder.append_non_null();
+                    self.string_tracker = Some((ht, hasher));
+                    return Ok(());
+                }
+                Entry::Vacant(vacant) => {
+                    // o.w. we insert the (string hash -> view index)
+                    // the idx is current length of views_builder, as we are 
inserting a new view
+                    vacant.insert(self.views_buffer.len());
                 }
-                self.string_tracker = Some((ht, hasher));
             }
+            self.string_tracker = Some((ht, hasher));
         }
 
         let required_cap = self.in_progress.len() + v.len();
diff --git a/arrow-avro/src/codec.rs b/arrow-avro/src/codec.rs
index fc101ae0b5..975b906cf7 100644
--- a/arrow-avro/src/codec.rs
+++ b/arrow-avro/src/codec.rs
@@ -218,12 +218,10 @@ impl AvroDataType {
     /// Returns an arrow [`Field`] with the given name
     pub(crate) fn field_with_name(&self, name: &str) -> Field {
         let mut nullable = self.nullability.is_some();
-        if !nullable {
-            if let Codec::Union(children, _, _) = self.codec() {
-                // If any encoded branch is `null`, mark field as nullable
-                if children.iter().any(|c| matches!(c.codec(), Codec::Null)) {
-                    nullable = true;
-                }
+        if !nullable && let Codec::Union(children, _, _) = self.codec() {
+            // If any encoded branch is `null`, mark field as nullable
+            if children.iter().any(|c| matches!(c.codec(), Codec::Null)) {
+                nullable = true;
             }
         }
         let data_type = self.codec.data_type();
@@ -282,13 +280,13 @@ impl AvroDataType {
                 }
                 out.push(cp as u8);
             }
-            if let Some(len) = expected_len {
-                if out.len() != len {
-                    return Err(ArrowError::SchemaError(format!(
-                        "Default length {} does not match expected fixed size 
{len}",
-                        out.len(),
-                    )));
-                }
+            if let Some(len) = expected_len
+                && out.len() != len
+            {
+                return Err(ArrowError::SchemaError(format!(
+                    "Default length {} does not match expected fixed size 
{len}",
+                    out.len(),
+                )));
             }
             Ok(out)
         }
@@ -1362,16 +1360,16 @@ fn union_first_duplicate<'a>(
 ) -> Option<String> {
     let mut seen = HashSet::with_capacity(branches.len());
     for schema in branches {
-        if let Some(key) = branch_key_of(schema, enclosing_ns) {
-            if !seen.insert(key.clone()) {
-                let msg = match key {
-                    UnionBranchKey::Named(full) => format!("named type 
{full}"),
-                    UnionBranchKey::Primitive(p) => format!("primitive {}", 
p.as_ref()),
-                    UnionBranchKey::Array => "array".to_string(),
-                    UnionBranchKey::Map => "map".to_string(),
-                };
-                return Some(msg);
-            }
+        if let Some(key) = branch_key_of(schema, enclosing_ns)
+            && !seen.insert(key.clone())
+        {
+            let msg = match key {
+                UnionBranchKey::Named(full) => format!("named type {full}"),
+                UnionBranchKey::Primitive(p) => format!("primitive {}", 
p.as_ref()),
+                UnionBranchKey::Array => "array".to_string(),
+                UnionBranchKey::Map => "map".to_string(),
+            };
+            return Some(msg);
         }
     }
     None
@@ -1777,17 +1775,15 @@ impl<'a> Maker<'a> {
                     }
                     (None, _) => {}
                 }
-                if matches!(field.codec, Codec::Int64) {
-                    if let Some(unit) = t
+                if matches!(field.codec, Codec::Int64)
+                    && let Some(unit) = t
                         .attributes
                         .additional
                         .get("arrowTimeUnit")
                         .and_then(|v| v.as_str())
-                    {
-                        if unit == "nanosecond" {
-                            field.codec = Codec::TimestampNanos(Some(self.tz));
-                        }
-                    }
+                    && unit == "nanosecond"
+                {
+                    field.codec = Codec::TimestampNanos(Some(self.tz));
                 }
                 if !t.attributes.additional.is_empty() {
                     for (k, v) in &t.attributes.additional {
diff --git a/arrow-avro/src/reader/mod.rs b/arrow-avro/src/reader/mod.rs
index a9ec035a70..874a8534d0 100644
--- a/arrow-avro/src/reader/mod.rs
+++ b/arrow-avro/src/reader/mod.rs
@@ -1744,10 +1744,10 @@ mod test {
                 }
                 Value::Array(arr) => {
                     for b in arr.iter_mut() {
-                        if let Value::Object(map) = b {
-                            if matches!(map.get("type"), 
Some(Value::String(t)) if t == "enum") {
-                                map.insert("symbols".to_string(), 
symbols.clone());
-                            }
+                        if let Value::Object(map) = b
+                            && matches!(map.get("type"), 
Some(Value::String(t)) if t == "enum")
+                        {
+                            map.insert("symbols".to_string(), symbols.clone());
                         }
                     }
                 }
@@ -8320,42 +8320,41 @@ mod test {
                 }
             }
             fn reverse_items_union(f: &mut Value) {
-                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut()) {
-                    if let Some(items) = obj.get_mut("items").and_then(|v| 
v.as_array_mut()) {
-                        items.reverse();
-                    }
+                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut())
+                    && let Some(items) = obj.get_mut("items").and_then(|v| 
v.as_array_mut())
+                {
+                    items.reverse();
                 }
             }
             fn reverse_map_values_union(f: &mut Value) {
-                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut()) {
-                    if let Some(values) = obj.get_mut("values").and_then(|v| 
v.as_array_mut()) {
-                        values.reverse();
-                    }
+                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut())
+                    && let Some(values) = obj.get_mut("values").and_then(|v| 
v.as_array_mut())
+                {
+                    values.reverse();
                 }
             }
             fn reverse_nested_union_in_record(f: &mut Value, field_name: &str) 
{
-                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut()) {
-                    if let Some(fields) = obj.get_mut("fields").and_then(|v| 
v.as_array_mut()) {
-                        for ff in fields.iter_mut() {
-                            if ff.get("name").and_then(|n| n.as_str()) == 
Some(field_name) {
-                                if let Some(ty) = ff.get_mut("type") {
-                                    if let Some(arr) = ty.as_array_mut() {
-                                        arr.reverse();
-                                    }
-                                }
-                            }
+                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut())
+                    && let Some(fields) = obj.get_mut("fields").and_then(|v| 
v.as_array_mut())
+                {
+                    for ff in fields.iter_mut() {
+                        if ff.get("name").and_then(|n| n.as_str()) == 
Some(field_name)
+                            && let Some(ty) = ff.get_mut("type")
+                            && let Some(arr) = ty.as_array_mut()
+                        {
+                            arr.reverse();
                         }
                     }
                 }
             }
             fn rename_nested_field_with_alias(f: &mut Value, old: &str, new: 
&str) {
-                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut()) {
-                    if let Some(fields) = obj.get_mut("fields").and_then(|v| 
v.as_array_mut()) {
-                        for ff in fields.iter_mut() {
-                            if ff.get("name").and_then(|n| n.as_str()) == 
Some(old) {
-                                ff["name"] = Value::String(new.to_string());
-                                ff["aliases"] = 
Value::Array(vec![Value::String(old.to_string())]);
-                            }
+                if let Some(obj) = f.get_mut("type").and_then(|t| 
t.as_object_mut())
+                    && let Some(fields) = obj.get_mut("fields").and_then(|v| 
v.as_array_mut())
+                {
+                    for ff in fields.iter_mut() {
+                        if ff.get("name").and_then(|n| n.as_str()) == 
Some(old) {
+                            ff["name"] = Value::String(new.to_string());
+                            ff["aliases"] = 
Value::Array(vec![Value::String(old.to_string())]);
                         }
                     }
                 }
diff --git a/arrow-avro/src/reader/record.rs b/arrow-avro/src/reader/record.rs
index 1d152870d7..23c7fed9bb 100644
--- a/arrow-avro/src/reader/record.rs
+++ b/arrow-avro/src/reader/record.rs
@@ -281,19 +281,20 @@ enum Decoder {
 
 impl Decoder {
     fn try_new(data_type: &AvroDataType) -> Result<Self, AvroError> {
-        if let Some(ResolutionInfo::Union(info)) = 
data_type.resolution.as_ref() {
-            if info.writer_is_union && !info.reader_is_union {
-                let mut clone = data_type.clone();
-                clone.resolution = None; // Build target base decoder without 
Union resolution
-                let target = Self::try_new_internal(&clone)?;
-                let decoder = Self::Union(
-                    UnionDecoderBuilder::new()
-                        .with_resolved_union(info.clone())
-                        .with_target(target)
-                        .build()?,
-                );
-                return Ok(decoder);
-            }
+        if let Some(ResolutionInfo::Union(info)) = 
data_type.resolution.as_ref()
+            && info.writer_is_union
+            && !info.reader_is_union
+        {
+            let mut clone = data_type.clone();
+            clone.resolution = None; // Build target base decoder without 
Union resolution
+            let target = Self::try_new_internal(&clone)?;
+            let decoder = Self::Union(
+                UnionDecoderBuilder::new()
+                    .with_resolved_union(info.clone())
+                    .with_target(target)
+                    .build()?,
+            );
+            return Ok(decoder);
         }
         Self::try_new_internal(data_type)
     }
@@ -550,10 +551,10 @@ impl Decoder {
                 let mut builder = UnionDecoderBuilder::new()
                     .with_fields(fields.clone())
                     .with_branches(decoders);
-                if let Some(ResolutionInfo::Union(info)) = 
data_type.resolution.as_ref() {
-                    if info.reader_is_union {
-                        builder = builder.with_resolved_union(info.clone());
-                    }
+                if let Some(ResolutionInfo::Union(info)) = 
data_type.resolution.as_ref()
+                    && info.reader_is_union
+                {
+                    builder = builder.with_resolved_union(info.clone());
                 }
                 Self::Union(builder.build()?)
             }
@@ -1631,13 +1632,13 @@ impl Decoder {
                     )));
                 }
                 let final_len = moff.len() - 1;
-                if let Some(n) = &nulls {
-                    if n.len() != final_len {
-                        return Err(AvroError::InvalidArgument(format!(
-                            "Map array null buffer length {} != final map 
length {final_len}",
-                            n.len()
-                        )));
-                    }
+                if let Some(n) = &nulls
+                    && n.len() != final_len
+                {
+                    return Err(AvroError::InvalidArgument(format!(
+                        "Map array null buffer length {} != final map length 
{final_len}",
+                        n.len()
+                    )));
                 }
                 let entries_fields = match map_field.data_type() {
                     DataType::Struct(fields) => fields.clone(),
diff --git a/arrow-avro/src/schema.rs b/arrow-avro/src/schema.rs
index 28965e5e99..db36e48a22 100644
--- a/arrow-avro/src/schema.rs
+++ b/arrow-avro/src/schema.rs
@@ -517,10 +517,8 @@ impl AvroSchema {
         let opts = options.unwrap_or_default();
         let order = opts.null_order.unwrap_or_default();
         let strip = opts.strip_metadata;
-        if !strip {
-            if let Some(json) = schema.metadata.get(SCHEMA_METADATA_KEY) {
-                return Ok(AvroSchema::new(json.clone()));
-            }
+        if !strip && let Some(json) = schema.metadata.get(SCHEMA_METADATA_KEY) 
{
+            return Ok(AvroSchema::new(json.clone()));
         }
         let mut name_gen = NameGenerator::default();
         let fields_json = schema
diff --git a/arrow-buffer/src/buffer/boolean.rs 
b/arrow-buffer/src/buffer/boolean.rs
index 52f1e3fbb5..591227a5c0 100644
--- a/arrow-buffer/src/buffer/boolean.rs
+++ b/arrow-buffer/src/buffer/boolean.rs
@@ -1035,7 +1035,7 @@ mod tests {
             .map(|i| (i as u8).wrapping_mul(37).wrapping_add(11))
             .collect::<Vec<_>>();
         let base = bytes.as_ptr() as usize;
-        let shift = (0..8).find(|s| (base + s) % 8 != 0).unwrap();
+        let shift = (0..8).find(|s| !(base + s).is_multiple_of(8)).unwrap();
         let misaligned = &bytes[shift..];
 
         // Case 1: fallback path with `remainder.is_empty() == true`
diff --git a/arrow-buffer/src/buffer/immutable.rs 
b/arrow-buffer/src/buffer/immutable.rs
index 9c7fc1a901..bfccdf5538 100644
--- a/arrow-buffer/src/buffer/immutable.rs
+++ b/arrow-buffer/src/buffer/immutable.rs
@@ -209,19 +209,19 @@ impl Buffer {
             // For realloc to work, we cannot free the elements before the 
offset
             offset + self.len()
         };
-        if desired_capacity < self.capacity() {
-            if let Some(bytes) = Arc::get_mut(&mut self.data) {
-                if bytes.try_realloc(desired_capacity).is_ok() {
-                    // Realloc complete - update our pointer into `bytes`:
-                    self.ptr = if is_empty {
-                        bytes.as_ptr()
-                    } else {
-                        // SAFETY: we kept all elements leading up to the 
offset
-                        unsafe { bytes.as_ptr().add(offset) }
-                    }
+        if desired_capacity < self.capacity()
+            && let Some(bytes) = Arc::get_mut(&mut self.data)
+        {
+            if bytes.try_realloc(desired_capacity).is_ok() {
+                // Realloc complete - update our pointer into `bytes`:
+                self.ptr = if is_empty {
+                    bytes.as_ptr()
                 } else {
-                    // Failure to reallocate is fine; we just failed to free 
up memory.
+                    // SAFETY: we kept all elements leading up to the offset
+                    unsafe { bytes.as_ptr().add(offset) }
                 }
+            } else {
+                // Failure to reallocate is fine; we just failed to free up 
memory.
             }
         }
     }
@@ -328,7 +328,7 @@ impl Buffer {
     /// If the offset is byte-aligned the returned buffer is a shallow clone,
     /// otherwise a new buffer is allocated and filled with a copy of the bits 
in the range.
     pub fn bit_slice(&self, offset: usize, len: usize) -> Self {
-        if offset % 8 == 0 {
+        if offset.is_multiple_of(8) {
             return self.slice_with_length(offset / 8, bit_util::ceil(len, 8));
         }
 
@@ -1047,7 +1047,7 @@ mod tests {
             // (since the `offset` value inside a Buffer is byte-granular, not 
bit-granular), so
             // checking the offset should always return 0 if so. If the offset 
IS byte-aligned, we
             // want to make sure it doesn't unnecessarily create a deep copy.
-            if offset % 8 == 0 {
+            if offset.is_multiple_of(8) {
                 assert_eq!(new_buf.ptr_offset(), offset / 8);
             } else {
                 assert_eq!(new_buf.ptr_offset(), 0);
diff --git a/arrow-csv/src/reader/mod.rs b/arrow-csv/src/reader/mod.rs
index 4d56d7e991..200766eeae 100644
--- a/arrow-csv/src/reader/mod.rs
+++ b/arrow-csv/src/reader/mod.rs
@@ -393,10 +393,10 @@ impl Format {
             // Note since we may be looking at a sample of the data, we make 
the safe assumption that
             // they could be nullable
             for (i, column_type) in 
column_types.iter_mut().enumerate().take(header_length) {
-                if let Some(string) = record.get(i) {
-                    if !self.null_regex.is_null(string) {
-                        column_type.update(string)
-                    }
+                if let Some(string) = record.get(i)
+                    && !self.null_regex.is_null(string)
+                {
+                    column_type.update(string)
                 }
             }
         }
diff --git a/arrow-data/src/data.rs b/arrow-data/src/data.rs
index 465c96352b..1c5d1bebd3 100644
--- a/arrow-data/src/data.rs
+++ b/arrow-data/src/data.rs
@@ -856,10 +856,10 @@ impl ArrayData {
     pub fn align_buffers(&mut self) {
         let layout = layout(&self.data_type);
         for (buffer, spec) in self.buffers.iter_mut().zip(&layout.buffers) {
-            if let BufferSpec::FixedWidth { alignment, .. } = spec {
-                if buffer.as_ptr().align_offset(*alignment) != 0 {
-                    *buffer = Buffer::from_slice_ref(buffer.as_ref());
-                }
+            if let BufferSpec::FixedWidth { alignment, .. } = spec
+                && buffer.as_ptr().align_offset(*alignment) != 0
+            {
+                *buffer = Buffer::from_slice_ref(buffer.as_ref());
             }
         }
         // align children data recursively
diff --git a/arrow-data/src/equal/boolean.rs b/arrow-data/src/equal/boolean.rs
index 64b7125e36..cbd18ad789 100644
--- a/arrow-data/src/equal/boolean.rs
+++ b/arrow-data/src/equal/boolean.rs
@@ -37,10 +37,10 @@ pub(super) fn boolean_equal(
 
     if !contains_nulls {
         // Optimize performance for starting offset at u8 boundary.
-        if lhs_start % 8 == 0
-            && rhs_start % 8 == 0
-            && lhs.offset() % 8 == 0
-            && rhs.offset() % 8 == 0
+        if lhs_start.is_multiple_of(8)
+            && rhs_start.is_multiple_of(8)
+            && lhs.offset().is_multiple_of(8)
+            && rhs.offset().is_multiple_of(8)
         {
             let quot = len / 8;
             if quot > 0
diff --git a/arrow-flight/src/decode.rs b/arrow-flight/src/decode.rs
index b0fc3da26d..fa0cc3f286 100644
--- a/arrow-flight/src/decode.rs
+++ b/arrow-flight/src/decode.rs
@@ -341,7 +341,8 @@ impl FlightDataDecoder {
                 let buffer = {
                     // see 
https://arrow.apache.org/docs/format/Columnar.html#buffer-alignment-and-padding
                     // reuse the allocation if already aligned, otherwise copy 
into a fresh aligned buffer.
-                    if data.data_body.as_ptr() as usize % 64 == 0 {
+                    let ptr_value = data.data_body.as_ptr() as usize;
+                    if ptr_value.is_multiple_of(64) {
                         &Buffer::from(data.data_body.clone())
                     } else {
                         &Buffer::from(data.data_body.as_ref())
diff --git a/arrow-flight/src/sql/client.rs b/arrow-flight/src/sql/client.rs
index 5476d4ede9..60ef575d1f 100644
--- a/arrow-flight/src/sql/client.rs
+++ b/arrow-flight/src/sql/client.rs
@@ -587,10 +587,9 @@ where
                 .await?
                 .message()
                 .await?
+                && let Some(handle) = 
self.unpack_prepared_statement_handle(&result)?
             {
-                if let Some(handle) = 
self.unpack_prepared_statement_handle(&result)? {
-                    self.handle = handle;
-                }
+                self.handle = handle;
             }
         }
         Ok(())
diff --git a/arrow-flight/tests/common/trailers_layer.rs 
b/arrow-flight/tests/common/trailers_layer.rs
index 0ccb7df86c..f78d1120c5 100644
--- a/arrow-flight/tests/common/trailers_layer.rs
+++ b/arrow-flight/tests/common/trailers_layer.rs
@@ -105,10 +105,10 @@ impl<B: http_body::Body> http_body::Body for 
WrappedBody<B> {
     ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
         let mut result = ready!(self.project().inner.poll_frame(cx));
 
-        if let Some(Ok(frame)) = &mut result {
-            if let Some(trailers) = frame.trailers_mut() {
-                trailers.insert("test-trailer", 
HeaderValue::from_static("trailer_val"));
-            }
+        if let Some(Ok(frame)) = &mut result
+            && let Some(trailers) = frame.trailers_mut()
+        {
+            trailers.insert("test-trailer", 
HeaderValue::from_static("trailer_val"));
         }
 
         Poll::Ready(result)
diff --git a/arrow-ipc/src/convert.rs b/arrow-ipc/src/convert.rs
index 5b65c81a94..391e73418d 100644
--- a/arrow-ipc/src/convert.rs
+++ b/arrow-ipc/src/convert.rs
@@ -213,10 +213,10 @@ pub fn fb_to_schema(fb: crate::Schema) -> Schema {
             let kv = md_fields.get(i);
             let k_str = kv.key();
             let v_str = kv.value();
-            if let Some(k) = k_str {
-                if let Some(v) = v_str {
-                    metadata.insert(k.to_string(), v.to_string());
-                }
+            if let Some(k) = k_str
+                && let Some(v) = v_str
+            {
+                metadata.insert(k.to_string(), v.to_string());
             }
         }
     }
@@ -292,25 +292,22 @@ pub fn try_schema_from_ipc_buffer(buffer: &[u8]) -> 
Result<Schema, ArrowError> {
 
 /// Get the Arrow data type from the flatbuffer Field table
 pub(crate) fn get_data_type(field: crate::Field, may_be_dictionary: bool) -> 
DataType {
-    if let Some(dictionary) = field.dictionary() {
-        if may_be_dictionary {
-            let int = dictionary.indexType().unwrap();
-            let index_type = match (int.bitWidth(), int.is_signed()) {
-                (8, true) => DataType::Int8,
-                (8, false) => DataType::UInt8,
-                (16, true) => DataType::Int16,
-                (16, false) => DataType::UInt16,
-                (32, true) => DataType::Int32,
-                (32, false) => DataType::UInt32,
-                (64, true) => DataType::Int64,
-                (64, false) => DataType::UInt64,
-                _ => panic!("Unexpected bitwidth and signed"),
-            };
-            return DataType::Dictionary(
-                Box::new(index_type),
-                Box::new(get_data_type(field, false)),
-            );
-        }
+    if let Some(dictionary) = field.dictionary()
+        && may_be_dictionary
+    {
+        let int = dictionary.indexType().unwrap();
+        let index_type = match (int.bitWidth(), int.is_signed()) {
+            (8, true) => DataType::Int8,
+            (8, false) => DataType::UInt8,
+            (16, true) => DataType::Int16,
+            (16, false) => DataType::UInt16,
+            (32, true) => DataType::Int32,
+            (32, false) => DataType::UInt32,
+            (64, true) => DataType::Int64,
+            (64, false) => DataType::UInt64,
+            _ => panic!("Unexpected bitwidth and signed"),
+        };
+        return DataType::Dictionary(Box::new(index_type), 
Box::new(get_data_type(field, false)));
     }
 
     match field.type_type() {
diff --git a/arrow-ipc/src/writer.rs b/arrow-ipc/src/writer.rs
index 4b46b7c790..8559b79b60 100644
--- a/arrow-ipc/src/writer.rs
+++ b/arrow-ipc/src/writer.rs
@@ -237,7 +237,7 @@ trait IpcMessageSinkExt: IpcMessageSink {
         write_options: &IpcWriteOptions,
     ) -> Result<(usize, usize), ArrowError> {
         let arrow_data_len = encoded.arrow_data.len();
-        if arrow_data_len % usize::from(write_options.alignment) != 0 {
+        if 
!arrow_data_len.is_multiple_of(usize::from(write_options.alignment)) {
             return Err(ArrowError::MemoryError(
                 "Arrow data not aligned".to_string(),
             ));
diff --git a/arrow-json/src/reader/value_iter.rs 
b/arrow-json/src/reader/value_iter.rs
index ebaba695ad..e4fbc14a83 100644
--- a/arrow-json/src/reader/value_iter.rs
+++ b/arrow-json/src/reader/value_iter.rs
@@ -66,10 +66,10 @@ impl<R: BufRead> Iterator for ValueIter<R> {
     type Item = Result<Value, ArrowError>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        if let Some(max) = self.max_read_records {
-            if self.record_count >= max {
-                return None;
-            }
+        if let Some(max) = self.max_read_records
+            && self.record_count >= max
+        {
+            return None;
         }
 
         loop {
diff --git a/arrow-json/src/writer/encoder.rs b/arrow-json/src/writer/encoder.rs
index ae0db947e4..e715ca4b4e 100644
--- a/arrow-json/src/writer/encoder.rs
+++ b/arrow-json/src/writer/encoder.rs
@@ -317,10 +317,10 @@ pub fn make_encoder<'a>(
         }};
     }
 
-    if let Some(factory) = options.encoder_factory() {
-        if let Some(encoder) = factory.make_default_encoder(field, array, 
options)? {
-            return Ok(encoder);
-        }
+    if let Some(factory) = options.encoder_factory()
+        && let Some(encoder) = factory.make_default_encoder(field, array, 
options)?
+    {
+        return Ok(encoder);
     }
 
     let nulls = array.nulls().cloned();
diff --git a/arrow-pyarrow-integration-testing/Cargo.toml 
b/arrow-pyarrow-integration-testing/Cargo.toml
index 46abd8ad3d..b5762484ec 100644
--- a/arrow-pyarrow-integration-testing/Cargo.toml
+++ b/arrow-pyarrow-integration-testing/Cargo.toml
@@ -25,7 +25,7 @@ authors = ["Apache Arrow <[email protected]>"]
 license = "Apache-2.0"
 keywords = ["arrow"]
 edition = "2024"
-rust-version = "1.85"
+rust-version = "1.88"
 publish = false
 
 [lib]
diff --git a/arrow-pyarrow-testing/Cargo.toml b/arrow-pyarrow-testing/Cargo.toml
index c5a15b5cc9..217e2b1448 100644
--- a/arrow-pyarrow-testing/Cargo.toml
+++ b/arrow-pyarrow-testing/Cargo.toml
@@ -40,7 +40,7 @@ authors = ["Apache Arrow <[email protected]>"]
 license = "Apache-2.0"
 keywords = ["arrow"]
 edition = "2024"
-rust-version = "1.85"
+rust-version = "1.88"
 publish = false
 
 
diff --git a/arrow-row/src/lib.rs b/arrow-row/src/lib.rs
index 3d0a20dbbd..e3edfa174d 100644
--- a/arrow-row/src/lib.rs
+++ b/arrow-row/src/lib.rs
@@ -2367,13 +2367,13 @@ unsafe fn decode_column(
                         let null_row_bytes: &[u8] = &null_rows[field_idx].data;
 
                         for idx in 0..len {
-                            if let Some((next_idx, bytes)) = 
field_row_iter.peek() {
-                                if *next_idx == idx {
-                                    sparse_data.push(*bytes);
+                            if let Some((next_idx, bytes)) = 
field_row_iter.peek()
+                                && *next_idx == idx
+                            {
+                                sparse_data.push(*bytes);
 
-                                    field_row_iter.next();
-                                    continue;
-                                }
+                                field_row_iter.next();
+                                continue;
                             }
                             sparse_data.push(null_row_bytes);
                         }
diff --git a/arrow-schema/src/schema.rs b/arrow-schema/src/schema.rs
index aa9ef877b1..809b2a33a5 100644
--- a/arrow-schema/src/schema.rs
+++ b/arrow-schema/src/schema.rs
@@ -294,13 +294,13 @@ impl Schema {
 
             // merge metadata
             for (key, value) in metadata.into_iter() {
-                if let Some(old_val) = out_meta.get(&key) {
-                    if old_val != &value {
-                        return Err(ArrowError::SchemaError(format!(
-                            "Fail to merge schema due to conflicting metadata. 
\
-                                         Key '{key}' has different values 
'{old_val}' and '{value}'"
-                        )));
-                    }
+                if let Some(old_val) = out_meta.get(&key)
+                    && old_val != &value
+                {
+                    return Err(ArrowError::SchemaError(format!(
+                        "Fail to merge schema due to conflicting metadata. \
+                                     Key '{key}' has different values 
'{old_val}' and '{value}'"
+                    )));
                 }
                 out_meta.insert(key, value);
             }
diff --git a/arrow-select/src/coalesce.rs b/arrow-select/src/coalesce.rs
index ad1fe2ab92..a4b552c293 100644
--- a/arrow-select/src/coalesce.rs
+++ b/arrow-select/src/coalesce.rs
@@ -449,30 +449,30 @@ impl BatchCoalescer {
         }
 
         // Large batch optimization: bypass coalescing for oversized batches
-        if let Some(limit) = self.biggest_coalesce_batch_size {
-            if batch_size > limit {
-                // Case 1: No buffered data - emit large batch directly
-                // Example: [] + [1200] → output [1200], buffer []
-                if self.buffered_rows == 0 {
-                    self.completed.push_back(batch);
-                    return Ok(());
-                }
-
-                // Case 2: Buffer too large - flush then emit to avoid 
oversized merge
-                // Example: [850] + [1200] → output [850], then output [1200]
-                // This prevents creating batches much larger than both 
target_batch_size
-                // and biggest_coalesce_batch_size, which could cause memory 
issues
-                if self.buffered_rows > limit {
-                    self.finish_buffered_batch()?;
-                    self.completed.push_back(batch);
-                    return Ok(());
-                }
+        if let Some(limit) = self.biggest_coalesce_batch_size
+            && batch_size > limit
+        {
+            // Case 1: No buffered data - emit large batch directly
+            // Example: [] + [1200] → output [1200], buffer []
+            if self.buffered_rows == 0 {
+                self.completed.push_back(batch);
+                return Ok(());
+            }
 
-                // Case 3: Small buffer - proceed with normal coalescing
-                // Example: [300] + [1200] → split and merge normally
-                // This ensures small batches still get properly coalesced
-                // while allowing some controlled growth beyond the limit
+            // Case 2: Buffer too large - flush then emit to avoid oversized 
merge
+            // Example: [850] + [1200] → output [850], then output [1200]
+            // This prevents creating batches much larger than both 
target_batch_size
+            // and biggest_coalesce_batch_size, which could cause memory issues
+            if self.buffered_rows > limit {
+                self.finish_buffered_batch()?;
+                self.completed.push_back(batch);
+                return Ok(());
             }
+
+            // Case 3: Small buffer - proceed with normal coalescing
+            // Example: [300] + [1200] → split and merge normally
+            // This ensures small batches still get properly coalesced
+            // while allowing some controlled growth beyond the limit
         }
 
         let (_schema, arrays, mut num_rows) = batch.into_parts();
diff --git a/arrow-select/src/dictionary.rs b/arrow-select/src/dictionary.rs
index 5b32f4e761..2f0418e1dc 100644
--- a/arrow-select/src/dictionary.rs
+++ b/arrow-select/src/dictionary.rs
@@ -82,9 +82,7 @@ pub fn garbage_collect_dictionary<K: ArrowDictionaryKeyType>(
 pub fn garbage_collect_any_dictionary(
     dictionary: &dyn AnyDictionaryArray,
 ) -> Result<ArrayRef, ArrowError> {
-    // FIXME: this is a workaround for MSRV Rust versions below 1.86 where 
trait upcasting is not stable.
-    // From 1.86 onward, `&dyn AnyDictionaryArray` can be directly passed to 
`downcast_dictionary_array!`.
-    let dictionary = &*dictionary.slice(0, dictionary.len());
+    let dictionary: &dyn Array = dictionary;
     downcast_dictionary_array!(
         dictionary => garbage_collect_dictionary(dictionary).map(|dict| 
Arc::new(dict) as ArrayRef),
         _ => unreachable!("have a dictionary array")
diff --git a/arrow-string/src/concat_elements.rs 
b/arrow-string/src/concat_elements.rs
index 8def0d5abb..72898793b8 100644
--- a/arrow-string/src/concat_elements.rs
+++ b/arrow-string/src/concat_elements.rs
@@ -343,14 +343,14 @@ where
         self,
         null_buffer: Option<NullBuffer>,
     ) -> Result<GenericByteViewArray<T>, ArrowError> {
-        if let Some(ref nulls) = null_buffer {
-            if nulls.len() != self.views.len() {
-                return Err(ArrowError::ComputeError(format!(
-                    "Null buffer length ({}) must match row count ({})",
-                    nulls.len(),
-                    self.views.len()
-                )));
-            }
+        if let Some(ref nulls) = null_buffer
+            && nulls.len() != self.views.len()
+        {
+            return Err(ArrowError::ComputeError(format!(
+                "Null buffer length ({}) must match row count ({})",
+                nulls.len(),
+                self.views.len()
+            )));
         }
 
         let buffers = if self.data.is_empty() {
diff --git a/arrow/src/tensor.rs b/arrow/src/tensor.rs
index 3b65ea7b52..812a7f8972 100644
--- a/arrow/src/tensor.rs
+++ b/arrow/src/tensor.rs
@@ -177,20 +177,20 @@ impl<'a, T: ArrowPrimitiveType> Tensor<'a, T> {
             }
 
             Some(ref s) => {
-                if let Some(ref st) = strides {
-                    if st.len() != s.len() {
-                        return Err(ArrowError::InvalidArgumentError(
-                            "shape and stride dimensions differ".to_string(),
-                        ));
-                    }
+                if let Some(ref st) = strides
+                    && st.len() != s.len()
+                {
+                    return Err(ArrowError::InvalidArgumentError(
+                        "shape and stride dimensions differ".to_string(),
+                    ));
                 }
 
-                if let Some(ref n) = names {
-                    if n.len() != s.len() {
-                        return Err(ArrowError::InvalidArgumentError(
-                            "number of dimensions and number of dimension 
names differ".to_string(),
-                        ));
-                    }
+                if let Some(ref n) = names
+                    && n.len() != s.len()
+                {
+                    return Err(ArrowError::InvalidArgumentError(
+                        "number of dimensions and number of dimension names 
differ".to_string(),
+                    ));
                 }
 
                 let total_elements: usize = s.iter().product();
diff --git a/parquet-variant-compute/src/variant_array.rs 
b/parquet-variant-compute/src/variant_array.rs
index ef72359ed7..f8bce42b44 100644
--- a/parquet-variant-compute/src/variant_array.rs
+++ b/parquet-variant-compute/src/variant_array.rs
@@ -1143,10 +1143,10 @@ fn typed_value_to_variant(typed_value: &ArrayRef, 
index: usize) -> Result<Varian
 /// verify that all data types in the struct are legal for a variant array.
 fn canonicalize_shredded_types(array: &dyn Array) -> Result<ArrayRef> {
     let new_type = canonicalize_and_verify_data_type(array.data_type())?;
-    if let Cow::Borrowed(_) = new_type {
-        if let Some(array) = array.as_struct_opt() {
-            return Ok(Arc::new(array.clone())); // bypass the unnecessary cast
-        }
+    if let Cow::Borrowed(_) = new_type
+        && let Some(array) = array.as_struct_opt()
+    {
+        return Ok(Arc::new(array.clone())); // bypass the unnecessary cast
     }
     cast(array, new_type.as_ref())
 }
diff --git a/parquet-variant-compute/src/variant_get.rs 
b/parquet-variant-compute/src/variant_get.rs
index bd28f5c8b7..35d94b70f4 100644
--- a/parquet-variant-compute/src/variant_get.rs
+++ b/parquet-variant-compute/src/variant_get.rs
@@ -343,26 +343,26 @@ fn shredded_get_path(
     //
     // For shredded/partially-shredded targets (`typed_value` present), 
recurse into each field
     // separately to take advantage of deeper shredding in child fields.
-    if !as_field.has_valid_extension_type::<VariantType>() {
-        if let DataType::Struct(fields) = as_field.data_type() {
-            if target.typed_value_column().is_none() {
-                return shred_basic_variant(target, VariantPath::default(), 
Some(as_field));
-            }
-
-            let children = fields
-                .iter()
-                .map(|field| {
-                    let path = 
&[VariantPathElement::from(field.name().as_str())];
-                    shredded_get_path(&target, path, Some(field), cast_options)
-                })
-                .collect::<Result<Vec<_>>>()?;
-
-            return Ok(Arc::new(StructArray::try_new(
-                fields.clone(),
-                children,
-                target.nulls().cloned(),
-            )?));
+    if !as_field.has_valid_extension_type::<VariantType>()
+        && let DataType::Struct(fields) = as_field.data_type()
+    {
+        if target.typed_value_column().is_none() {
+            return shred_basic_variant(target, VariantPath::default(), 
Some(as_field));
         }
+
+        let children = fields
+            .iter()
+            .map(|field| {
+                let path = &[VariantPathElement::from(field.name().as_str())];
+                shredded_get_path(&target, path, Some(field), cast_options)
+            })
+            .collect::<Result<Vec<_>>>()?;
+
+        return Ok(Arc::new(StructArray::try_new(
+            fields.clone(),
+            children,
+            target.nulls().cloned(),
+        )?));
     }
 
     // Not a struct, so directly shred the variant as the requested type
diff --git a/parquet-variant/src/variant.rs b/parquet-variant/src/variant.rs
index c9f175c3a6..dc1d833703 100644
--- a/parquet-variant/src/variant.rs
+++ b/parquet-variant/src/variant.rs
@@ -1772,7 +1772,7 @@ impl From<NaiveDate> for Variant<'_, '_> {
 
 impl From<DateTime<Utc>> for Variant<'_, '_> {
     fn from(value: DateTime<Utc>) -> Self {
-        if value.nanosecond() % 1000 > 0 {
+        if !value.nanosecond().is_multiple_of(1000) {
             Variant::TimestampNanos(value)
         } else {
             Variant::TimestampMicros(value)
@@ -1782,7 +1782,7 @@ impl From<DateTime<Utc>> for Variant<'_, '_> {
 
 impl From<NaiveDateTime> for Variant<'_, '_> {
     fn from(value: NaiveDateTime) -> Self {
-        if value.nanosecond() % 1000 > 0 {
+        if !value.nanosecond().is_multiple_of(1000) {
             Variant::TimestampNtzNanos(value)
         } else {
             Variant::TimestampNtzMicros(value)
diff --git a/parquet-variant/src/variant/metadata.rs 
b/parquet-variant/src/variant/metadata.rs
index d5d08d204c..a1226a8197 100644
--- a/parquet-variant/src/variant/metadata.rs
+++ b/parquet-variant/src/variant/metadata.rs
@@ -297,12 +297,12 @@ impl<'m> VariantMetadata<'m> {
                         },
                     )?;
 
-                    if let Some(prev_val) = prev_value {
-                        if current_value <= prev_val {
-                            return Err(ArrowError::InvalidArgumentError(
-                                "dictionary values are not unique and 
ordered".to_string(),
-                            ));
-                        }
+                    if let Some(prev_val) = prev_value
+                        && current_value <= prev_val
+                    {
+                        return Err(ArrowError::InvalidArgumentError(
+                            "dictionary values are not unique and 
ordered".to_string(),
+                        ));
                     }
 
                     prev_value = Some(current_value);
diff --git a/parquet-variant/src/variant/object.rs 
b/parquet-variant/src/variant/object.rs
index bb91584cef..cf563d9e35 100644
--- a/parquet-variant/src/variant/object.rs
+++ b/parquet-variant/src/variant/object.rs
@@ -266,12 +266,12 @@ impl<'m, 'v> VariantObject<'m, 'v> {
                 for field_id in field_ids_iter {
                     let next_field_name = self.metadata.get(field_id)?;
 
-                    if let Some(current_name) = current_field_name {
-                        if next_field_name < current_name {
-                            return Err(ArrowError::InvalidArgumentError(
-                                "field names not sorted".to_string(),
-                            ));
-                        }
+                    if let Some(current_name) = current_field_name
+                        && next_field_name < current_name
+                    {
+                        return Err(ArrowError::InvalidArgumentError(
+                            "field names not sorted".to_string(),
+                        ));
                     }
                     current_field_name = Some(next_field_name);
                 }
diff --git a/parquet/benches/row_selector.rs b/parquet/benches/row_selector.rs
index a29a1bf852..b2b3b55899 100644
--- a/parquet/benches/row_selector.rs
+++ b/parquet/benches/row_selector.rs
@@ -46,7 +46,7 @@ fn generate_random_row_selection(total_rows: usize, 
selection_ratio: f64) -> Boo
 
 /// Generates a mask alternating between selected and skipped runs of 
`run_len` rows.
 fn generate_run_length_mask(total_rows: usize, run_len: usize) -> 
BooleanBuffer {
-    BooleanBuffer::from_iter((0..total_rows).map(|row| (row / run_len) % 2 == 
0))
+    BooleanBuffer::from_iter((0..total_rows).map(|row| (row / 
run_len).is_multiple_of(2)))
 }
 
 /// Benchmarks converting a mask-backed [`RowSelection`] into [`RowSelector`]s.
diff --git a/parquet/src/arrow/array_reader/fixed_len_byte_array.rs 
b/parquet/src/arrow/array_reader/fixed_len_byte_array.rs
index e895f79921..91e5076f45 100644
--- a/parquet/src/arrow/array_reader/fixed_len_byte_array.rs
+++ b/parquet/src/arrow/array_reader/fixed_len_byte_array.rs
@@ -462,15 +462,13 @@ impl ColumnValueDecoder for ValueDecoder {
             Some(x) => assert_eq!(x, self.byte_length),
             None => {
                 out.byte_length = Some(self.byte_length);
-                // TODO: collapse to a let-chain once MSRV ≥ 1.88
-                // (`if out.buffer.is_empty() && let Some(cap) = 
out.values_capacity.take()`)
-                if out.buffer.is_empty() {
-                    if let Some(values_capacity) = out.values_capacity.take() {
-                        // now that the byte length per output element is 
known,
-                        // allocate the actual needed space.
-                        let byte_capacity = 
values_capacity.saturating_mul(self.byte_length);
-                        out.buffer = Vec::with_capacity(byte_capacity);
-                    }
+                if out.buffer.is_empty()
+                    && let Some(values_capacity) = out.values_capacity.take()
+                {
+                    // now that the byte length per output element is known,
+                    // allocate the actual needed space.
+                    let byte_capacity = 
values_capacity.saturating_mul(self.byte_length);
+                    out.buffer = Vec::with_capacity(byte_capacity);
                 }
             }
         }
diff --git a/parquet/src/arrow/array_reader/fixed_size_list_array.rs 
b/parquet/src/arrow/array_reader/fixed_size_list_array.rs
index 976aeb98ba..fadc934ef3 100644
--- a/parquet/src/arrow/array_reader/fixed_size_list_array.rs
+++ b/parquet/src/arrow/array_reader/fixed_size_list_array.rs
@@ -111,10 +111,10 @@ impl ArrayReader for FixedSizeListArrayReader {
         let mut boundary_defs: Vec<i16> = Vec::with_capacity(def_levels.len());
         let mut current_row_len = None;
         for (&d, &r) in def_levels.iter().zip(rep_levels) {
-            if let Some(threshold) = self.parent_threshold {
-                if d < threshold {
-                    continue;
-                }
+            if let Some(threshold) = self.parent_threshold
+                && d < threshold
+            {
+                continue;
             }
             if r < self.rep_level {
                 if let Some(row_len) = current_row_len.take().filter(|&len| 
len != self.fixed_size)
diff --git a/parquet/src/arrow/arrow_reader/read_plan.rs 
b/parquet/src/arrow/arrow_reader/read_plan.rs
index 92ebbe9bae..de2ef875ad 100644
--- a/parquet/src/arrow/arrow_reader/read_plan.rs
+++ b/parquet/src/arrow/arrow_reader/read_plan.rs
@@ -259,11 +259,11 @@ impl ReadPlanBuilder {
         // reader would have produced — rows past the early break are marked
         // "not selected". When no limit is set the loop always exhausts and
         // no padding is needed.
-        if let Some(expected) = expected_rows {
-            if processed_rows < expected {
-                let pad_len = expected - processed_rows;
-                
filters.push(BooleanArray::new(BooleanBuffer::new_unset(pad_len), None));
-            }
+        if let Some(expected) = expected_rows
+            && processed_rows < expected
+        {
+            let pad_len = expected - processed_rows;
+            filters.push(BooleanArray::new(BooleanBuffer::new_unset(pad_len), 
None));
         }
 
         // If the predicate selected all rows, applying it is a no-op. With no
diff --git a/parquet/src/arrow/arrow_writer/levels.rs 
b/parquet/src/arrow/arrow_writer/levels.rs
index 2781898694..cbac5cfe54 100644
--- a/parquet/src/arrow/arrow_writer/levels.rs
+++ b/parquet/src/arrow/arrow_writer/levels.rs
@@ -848,11 +848,12 @@ impl LevelInfoBuilder {
         let len = range.end - range.start;
 
         // Fast path: entire leaf array is null
-        if let Some(nulls) = &info.logical_nulls {
-            if !matches!(info.def_levels, LevelData::Absent) && 
nulls.null_count() == nulls.len() {
-                info.extend_uniform_levels(info.max_def_level - 1, 
info.max_rep_level, len);
-                return;
-            }
+        if let Some(nulls) = &info.logical_nulls
+            && !matches!(info.def_levels, LevelData::Absent)
+            && nulls.null_count() == nulls.len()
+        {
+            info.extend_uniform_levels(info.max_def_level - 1, 
info.max_rep_level, len);
+            return;
         }
 
         if matches!(info.def_levels, LevelData::Absent) {
diff --git a/parquet/src/arrow/arrow_writer/mod.rs 
b/parquet/src/arrow/arrow_writer/mod.rs
index 58577e1bd3..79b8ebe8be 100644
--- a/parquet/src/arrow/arrow_writer/mod.rs
+++ b/parquet/src/arrow/arrow_writer/mod.rs
@@ -368,45 +368,45 @@ impl<W: Write + Send> ArrowWriter<W> {
             ),
         };
 
-        if let Some(max_rows) = self.max_row_group_row_count {
-            if in_progress.buffered_rows + batch.num_rows() > max_rows {
-                let to_write = max_rows - in_progress.buffered_rows;
-                let a = batch.slice(0, to_write);
-                let b = batch.slice(to_write, batch.num_rows() - to_write);
-                self.write(&a)?;
-                return self.write(&b);
-            }
+        if let Some(max_rows) = self.max_row_group_row_count
+            && in_progress.buffered_rows + batch.num_rows() > max_rows
+        {
+            let to_write = max_rows - in_progress.buffered_rows;
+            let a = batch.slice(0, to_write);
+            let b = batch.slice(to_write, batch.num_rows() - to_write);
+            self.write(&a)?;
+            return self.write(&b);
         }
 
         // Check byte limit: if we have buffered data, use measured average 
row size
         // to split batch proactively before exceeding byte limit
-        if let Some(max_bytes) = self.max_row_group_bytes {
-            if in_progress.buffered_rows > 0 {
-                let current_bytes = in_progress.get_estimated_total_bytes();
+        if let Some(max_bytes) = self.max_row_group_bytes
+            && in_progress.buffered_rows > 0
+        {
+            let current_bytes = in_progress.get_estimated_total_bytes();
 
-                if current_bytes >= max_bytes {
-                    self.flush()?;
-                    return self.write(batch);
-                }
+            if current_bytes >= max_bytes {
+                self.flush()?;
+                return self.write(batch);
+            }
 
-                if let Some(avg_row_bytes) = current_bytes
-                    .checked_div(in_progress.buffered_rows)
-                    .filter(|avg_row_bytes| *avg_row_bytes > 0)
-                {
-                    // At this point, `current_bytes < max_bytes` (checked 
above)
-                    let remaining_bytes = max_bytes - current_bytes;
-                    let rows_that_fit = 
remaining_bytes.checked_div(avg_row_bytes).unwrap_or(0);
-
-                    if batch.num_rows() > rows_that_fit {
-                        if rows_that_fit > 0 {
-                            let a = batch.slice(0, rows_that_fit);
-                            let b = batch.slice(rows_that_fit, 
batch.num_rows() - rows_that_fit);
-                            self.write(&a)?;
-                            return self.write(&b);
-                        } else {
-                            self.flush()?;
-                            return self.write(batch);
-                        }
+            if let Some(avg_row_bytes) = current_bytes
+                .checked_div(in_progress.buffered_rows)
+                .filter(|avg_row_bytes| *avg_row_bytes > 0)
+            {
+                // At this point, `current_bytes < max_bytes` (checked above)
+                let remaining_bytes = max_bytes - current_bytes;
+                let rows_that_fit = 
remaining_bytes.checked_div(avg_row_bytes).unwrap_or(0);
+
+                if batch.num_rows() > rows_that_fit {
+                    if rows_that_fit > 0 {
+                        let a = batch.slice(0, rows_that_fit);
+                        let b = batch.slice(rows_that_fit, batch.num_rows() - 
rows_that_fit);
+                        self.write(&a)?;
+                        return self.write(&b);
+                    } else {
+                        self.flush()?;
+                        return self.write(batch);
                     }
                 }
             }
diff --git a/parquet/src/arrow/async_reader/store.rs 
b/parquet/src/arrow/async_reader/store.rs
index fe9cb65bfe..0b1efc492b 100644
--- a/parquet/src/arrow/async_reader/store.rs
+++ b/parquet/src/arrow/async_reader/store.rs
@@ -236,14 +236,13 @@ impl AsyncFileReader for ParquetObjectReader {
             // When page_index_policy is Skip (default), use the reader's 
preload flags.
             // When page_index_policy is Optional or Required, override the 
preload flags
             // to ensure the specified policy takes precedence.
-            if let Some(options) = options {
-                if options.column_index_policy() != PageIndexPolicy::Skip
-                    || options.offset_index_policy() != PageIndexPolicy::Skip
-                {
-                    metadata = metadata
-                        
.with_column_index_policy(options.column_index_policy())
-                        
.with_offset_index_policy(options.offset_index_policy());
-                }
+            if let Some(options) = options
+                && (options.column_index_policy() != PageIndexPolicy::Skip
+                    || options.offset_index_policy() != PageIndexPolicy::Skip)
+            {
+                metadata = metadata
+                    .with_column_index_policy(options.column_index_policy())
+                    .with_offset_index_policy(options.offset_index_policy());
             }
 
             let metadata = if let Some(file_size) = self.file_size {
diff --git a/parquet/src/arrow/buffer/offset_buffer.rs 
b/parquet/src/arrow/buffer/offset_buffer.rs
index ab67694c29..1f940f7cfd 100644
--- a/parquet/src/arrow/buffer/offset_buffer.rs
+++ b/parquet/src/arrow/buffer/offset_buffer.rs
@@ -62,15 +62,13 @@ impl<I: OffsetSizeTrait> OffsetBuffer<I> {
     /// UTF-8. This should be done by calling [`Self::check_valid_utf8`] after
     /// all data has been written
     pub fn try_push(&mut self, data: &[u8], validate_utf8: bool) -> Result<()> 
{
-        if validate_utf8 {
-            if let Some(&b) = data.first() {
-                // A valid code-point iff it does not start with 0b10xxxxxx
-                // Bit-magic taken from `std::str::is_char_boundary`
-                if (b as i8) < -0x40 {
-                    return Err(ParquetError::General(
-                        "encountered non UTF-8 data".to_string(),
-                    ));
-                }
+        if validate_utf8 && let Some(&b) = data.first() {
+            // A valid code-point iff it does not start with 0b10xxxxxx
+            // Bit-magic taken from `std::str::is_char_boundary`
+            if (b as i8) < -0x40 {
+                return Err(ParquetError::General(
+                    "encountered non UTF-8 data".to_string(),
+                ));
             }
         }
 
diff --git a/parquet/src/arrow/push_decoder/reader_builder/mod.rs 
b/parquet/src/arrow/push_decoder/reader_builder/mod.rs
index b773f95fd5..60b987828b 100644
--- a/parquet/src/arrow/push_decoder/reader_builder/mod.rs
+++ b/parquet/src/arrow/push_decoder/reader_builder/mod.rs
@@ -158,10 +158,10 @@ impl RowBudget {
             *offset = offset.saturating_sub(rows_before_budget - 
rows_after_budget);
         }
 
-        if rows_after_budget != 0 {
-            if let Some(limit) = &mut self.limit {
-                *limit -= rows_after_budget;
-            }
+        if rows_after_budget != 0
+            && let Some(limit) = &mut self.limit
+        {
+            *limit -= rows_after_budget;
         }
 
         self
diff --git a/parquet/src/arrow/record_reader/definition_levels.rs 
b/parquet/src/arrow/record_reader/definition_levels.rs
index a104c93a2c..b4182b166e 100644
--- a/parquet/src/arrow/record_reader/definition_levels.rs
+++ b/parquet/src/arrow/record_reader/definition_levels.rs
@@ -173,17 +173,16 @@ pub(crate) fn build_filtered_validity_bitmap(
         let mut value_mask: u64 = 0;
         for (i, &d) in chunk.iter().enumerate() {
             let mut include = true;
-            if let Some(threshold) = include_threshold {
-                if d < threshold {
-                    include = false;
-                }
+            if let Some(threshold) = include_threshold
+                && d < threshold
+            {
+                include = false;
             }
-            if include {
-                if let Some((reps, max_rep)) = rep_filter {
-                    if reps[base + i] > max_rep {
-                        include = false;
-                    }
-                }
+            if include
+                && let Some((reps, max_rep)) = rep_filter
+                && reps[base + i] > max_rep
+            {
+                include = false;
             }
             include_mask |= (include as u64) << i;
             value_mask |= ((d >= value_level) as u64) << i;
@@ -197,15 +196,15 @@ pub(crate) fn build_filtered_validity_bitmap(
 
     for idx in remainder_offset..def_levels.len() {
         let d = def_levels[idx];
-        if let Some(threshold) = include_threshold {
-            if d < threshold {
-                continue;
-            }
+        if let Some(threshold) = include_threshold
+            && d < threshold
+        {
+            continue;
         }
-        if let Some((reps, max_rep)) = rep_filter {
-            if reps[idx] > max_rep {
-                continue;
-            }
+        if let Some((reps, max_rep)) = rep_filter
+            && reps[idx] > max_rep
+        {
+            continue;
         }
         bitmap.append(d >= value_level);
         item_count += 1;
diff --git a/parquet/src/column/chunker/cdc.rs 
b/parquet/src/column/chunker/cdc.rs
index b40dd74a8d..3f5b6afece 100644
--- a/parquet/src/column/chunker/cdc.rs
+++ b/parquet/src/column/chunker/cdc.rs
@@ -811,17 +811,17 @@ mod arrow_tests {
                     let arr: BooleanArray = (0..length)
                         .map(|i| {
                             let val = test_hash(seed, i as u64);
-                            if val % 10 == 0 {
+                            if val.is_multiple_of(10) {
                                 None
                             } else {
-                                Some(val % 2 == 0)
+                                Some(val.is_multiple_of(2))
                             }
                         })
                         .collect();
                     Arc::new(arr)
                 } else {
                     let arr: BooleanArray = (0..length)
-                        .map(|i| Some(test_hash(seed, i as u64) % 2 == 0))
+                        .map(|i| Some(test_hash(seed, i as 
u64).is_multiple_of(2)))
                         .collect();
                     Arc::new(arr)
                 }
@@ -839,7 +839,7 @@ mod arrow_tests {
                     (0..length)
                         .map(|i| {
                             let val = test_hash(seed, i as u64);
-                            if val % 10 == 0 {
+                            if val.is_multiple_of(10) {
                                 None
                             } else {
                                 Some(format!("str_{val}"))
@@ -858,7 +858,7 @@ mod arrow_tests {
                     (0..length)
                         .map(|i| {
                             let val = test_hash(seed, i as u64);
-                            if val % 10 == 0 {
+                            if val.is_multiple_of(10) {
                                 None
                             } else {
                                 Some(format!("bin_{val}").into_bytes())
@@ -877,7 +877,7 @@ mod arrow_tests {
                 let mut builder = 
arrow_array::builder::FixedSizeBinaryBuilder::new(size);
                 for i in 0..length {
                     let val = test_hash(seed, i as u64);
-                    if nullable && val % 10 == 0 {
+                    if nullable && val.is_multiple_of(10) {
                         builder.append_null();
                     } else {
                         let s = format!("bin_{val}");
diff --git a/parquet/src/column/reader.rs b/parquet/src/column/reader.rs
index 81960abbe4..0bf5dd822d 100644
--- a/parquet/src/column/reader.rs
+++ b/parquet/src/column/reader.rs
@@ -333,12 +333,12 @@ where
                         .then_some(metadata.num_levels)?
                 });
 
-                if let Some(rows) = rows {
-                    if rows <= remaining_records {
-                        self.page_reader.skip_next_page()?;
-                        remaining_records -= rows;
-                        continue;
-                    }
+                if let Some(rows) = rows
+                    && rows <= remaining_records
+                {
+                    self.page_reader.skip_next_page()?;
+                    remaining_records -= rows;
+                    continue;
                 }
                 // because self.num_buffered_values == self.num_decoded_values 
means
                 // we need reads a new page and set up the decoders for levels
diff --git a/parquet/src/encodings/decoding.rs 
b/parquet/src/encodings/decoding.rs
index 0fb9604122..1e117799ad 100644
--- a/parquet/src/encodings/decoding.rs
+++ b/parquet/src/encodings/decoding.rs
@@ -703,14 +703,14 @@ where
         self.first_value =
             Some(T::T::from_i64(first_value).ok_or_else(|| general_err!("first 
value too large"))?);
 
-        if self.block_size % 128 != 0 {
+        if !self.block_size.is_multiple_of(128) {
             return Err(general_err!(
                 "'block_size' must be a multiple of 128, got {}",
                 self.block_size
             ));
         }
 
-        if self.block_size % self.mini_blocks_per_block != 0 {
+        if !self.block_size.is_multiple_of(self.mini_blocks_per_block) {
             return Err(general_err!(
                 "'block_size' must be a multiple of 'mini_blocks_per_block' 
got {} and {}",
                 self.block_size,
@@ -724,7 +724,7 @@ where
         self.mini_block_remaining = 0;
         self.mini_block_bit_widths.clear();
 
-        if self.values_per_mini_block % 32 != 0 {
+        if !self.values_per_mini_block.is_multiple_of(32) {
             return Err(general_err!(
                 "'values_per_mini_block' must be a multiple of 32 got {}",
                 self.values_per_mini_block
diff --git a/parquet/src/encodings/decoding/byte_stream_split_decoder.rs 
b/parquet/src/encodings/decoding/byte_stream_split_decoder.rs
index b72ae8f62c..2e26e4d3aa 100644
--- a/parquet/src/encodings/decoding/byte_stream_split_decoder.rs
+++ b/parquet/src/encodings/decoding/byte_stream_split_decoder.rs
@@ -160,7 +160,7 @@ impl<T: DataType> VariableWidthByteStreamSplitDecoder<T> {
 impl<T: DataType> Decoder<T> for VariableWidthByteStreamSplitDecoder<T> {
     fn set_data(&mut self, data: Bytes, num_values: usize) -> Result<()> {
         // Rough check that all data elements are the same length
-        if data.len() % self.type_width != 0 {
+        if !data.len().is_multiple_of(self.type_width) {
             return Err(general_err!(
                 "Input data length is not a multiple of type width {}",
                 self.type_width
diff --git a/parquet/src/file/metadata/reader.rs 
b/parquet/src/file/metadata/reader.rs
index 844ae747c7..d695610f20 100644
--- a/parquet/src/file/metadata/reader.rs
+++ b/parquet/src/file/metadata/reader.rs
@@ -569,10 +569,10 @@ impl ParquetMetaDataReader {
     /// file footer (8 bytes). Otherwise returns `8`.
     #[cfg(all(feature = "async", feature = "arrow"))]
     fn get_prefetch_size(&self) -> usize {
-        if let Some(prefetch) = self.prefetch_hint {
-            if prefetch > FOOTER_SIZE {
-                return prefetch;
-            }
+        if let Some(prefetch) = self.prefetch_hint
+            && prefetch > FOOTER_SIZE
+        {
+            return prefetch;
         }
         FOOTER_SIZE
     }
diff --git a/parquet/src/file/metadata/thrift/mod.rs 
b/parquet/src/file/metadata/thrift/mod.rs
index fdf0bf786a..ce4c9719f6 100644
--- a/parquet/src/file/metadata/thrift/mod.rs
+++ b/parquet/src/file/metadata/thrift/mod.rs
@@ -237,15 +237,15 @@ fn convert_stats(
             };
 
             fn check_len(min: &Option<&[u8]>, max: &Option<&[u8]>, len: usize) 
-> Result<()> {
-                if let Some(min) = min {
-                    if min.len() < len {
-                        return Err(general_err!("Insufficient bytes to parse 
min statistic",));
-                    }
+                if let Some(min) = min
+                    && min.len() < len
+                {
+                    return Err(general_err!("Insufficient bytes to parse min 
statistic",));
                 }
-                if let Some(max) = max {
-                    if max.len() < len {
-                        return Err(general_err!("Insufficient bytes to parse 
max statistic",));
-                    }
+                if let Some(max) = max
+                    && max.len() < len
+                {
+                    return Err(general_err!("Insufficient bytes to parse max 
statistic",));
                 }
                 Ok(())
             }
@@ -1620,12 +1620,11 @@ impl WriteThrift for RowGroupMetaData {
             .write_thrift_field(writer, 6, last_field_id)?;
 
         // write ordinal if it will fit in an i16
-        if writer.write_row_group_ordinal() {
-            if let Some(ordinal) = self.ordinal() {
-                if let Ok(ordinal) = i16::try_from(ordinal) {
-                    ordinal.write_thrift_field(writer, 7, last_field_id)?;
-                }
-            }
+        if writer.write_row_group_ordinal()
+            && let Some(ordinal) = self.ordinal()
+            && let Ok(ordinal) = i16::try_from(ordinal)
+        {
+            ordinal.write_thrift_field(writer, 7, last_field_id)?;
         }
         writer.write_struct_end()
     }
diff --git a/parquet/src/file/page_index/column_index.rs 
b/parquet/src/file/page_index/column_index.rs
index 63b94f28df..4b766cc972 100644
--- a/parquet/src/file/page_index/column_index.rs
+++ b/parquet/src/file/page_index/column_index.rs
@@ -123,27 +123,29 @@ impl<T: ParquetValueType> PrimitiveColumnIndex<T> {
                 max_bytes.len()
             )));
         }
-        if let Some(ref nc) = null_counts {
-            if nc.len() != len {
-                return Err(ParquetError::General(format!(
-                    "ColumnIndex null_counts length mismatch: expected {len}, 
got {}",
-                    nc.len()
-                )));
-            }
+        if let Some(ref nc) = null_counts
+            && nc.len() != len
+        {
+            return Err(ParquetError::General(format!(
+                "ColumnIndex null_counts length mismatch: expected {len}, got 
{}",
+                nc.len()
+            )));
         }
-        if let Some(ref rep) = repetition_level_histograms {
-            if len != 0 && rep.len() % len != 0 {
-                return Err(ParquetError::General(
-                    "Invalid repetition_level_histograms length".to_string(),
-                ));
-            }
+        if let Some(ref rep) = repetition_level_histograms
+            && len != 0
+            && rep.len() % len != 0
+        {
+            return Err(ParquetError::General(
+                "Invalid repetition_level_histograms length".to_string(),
+            ));
         }
-        if let Some(ref def) = definition_level_histograms {
-            if len != 0 && def.len() % len != 0 {
-                return Err(ParquetError::General(
-                    "Invalid definition_level_histograms length".to_string(),
-                ));
-            }
+        if let Some(ref def) = definition_level_histograms
+            && len != 0
+            && def.len() % len != 0
+        {
+            return Err(ParquetError::General(
+                "Invalid definition_level_histograms length".to_string(),
+            ));
         }
 
         let mut min_values = Vec::with_capacity(len);
@@ -340,27 +342,29 @@ impl ByteArrayColumnIndex {
                 max_values.len()
             )));
         }
-        if let Some(ref nc) = null_counts {
-            if nc.len() != len {
-                return Err(ParquetError::General(format!(
-                    "ColumnIndex null_counts length mismatch: expected {len}, 
got {}",
-                    nc.len()
-                )));
-            }
-        }
-        if let Some(ref rep) = repetition_level_histograms {
-            if len != 0 && rep.len() % len != 0 {
-                return Err(ParquetError::General(
-                    "Invalid repetition_level_histograms length".to_string(),
-                ));
-            }
+        if let Some(ref nc) = null_counts
+            && nc.len() != len
+        {
+            return Err(ParquetError::General(format!(
+                "ColumnIndex null_counts length mismatch: expected {len}, got 
{}",
+                nc.len()
+            )));
         }
-        if let Some(ref def) = definition_level_histograms {
-            if len != 0 && def.len() % len != 0 {
-                return Err(ParquetError::General(
-                    "Invalid definition_level_histograms length".to_string(),
-                ));
-            }
+        if let Some(ref rep) = repetition_level_histograms
+            && len != 0
+            && rep.len() % len != 0
+        {
+            return Err(ParquetError::General(
+                "Invalid repetition_level_histograms length".to_string(),
+            ));
+        }
+        if let Some(ref def) = definition_level_histograms
+            && len != 0
+            && def.len() % len != 0
+        {
+            return Err(ParquetError::General(
+                "Invalid definition_level_histograms length".to_string(),
+            ));
         }
 
         let min_len = min_values.iter().map(|&v| v.len()).sum();
diff --git a/parquet/src/file/properties.rs b/parquet/src/file/properties.rs
index 19191f6018..42b0e124a5 100644
--- a/parquet/src/file/properties.rs
+++ b/parquet/src/file/properties.rs
@@ -1755,10 +1755,10 @@ impl ColumnProperties {
     /// If bloom filter is enabled and NDV was not explicitly set, resolve it 
to the
     /// given `default_ndv` (typically derived from `max_row_group_row_count`).
     fn resolve_bloom_filter_ndv(&mut self, default_ndv: u64) {
-        if !self.bloom_filter_ndv_is_set {
-            if let Some(ref mut bf) = self.bloom_filter_properties {
-                bf.ndv = default_ndv;
-            }
+        if !self.bloom_filter_ndv_is_set
+            && let Some(ref mut bf) = self.bloom_filter_properties
+        {
+            bf.ndv = default_ndv;
         }
     }
 }
diff --git a/parquet/src/file/serialized_reader.rs 
b/parquet/src/file/serialized_reader.rs
index 113e5203c9..7c9b33132e 100644
--- a/parquet/src/file/serialized_reader.rs
+++ b/parquet/src/file/serialized_reader.rs
@@ -283,10 +283,10 @@ impl<R: 'static + ChunkReader> SerializedFileReader<R> {
 fn get_midpoint_offset(meta: &RowGroupMetaData) -> i64 {
     let col = meta.column(0);
     let mut offset = col.data_page_offset();
-    if let Some(dic_offset) = col.dictionary_page_offset() {
-        if offset > dic_offset {
-            offset = dic_offset
-        }
+    if let Some(dic_offset) = col.dictionary_page_offset()
+        && offset > dic_offset
+    {
+        offset = dic_offset
     };
     offset + meta.compressed_size() / 2
 }
diff --git a/parquet/src/file/statistics.rs b/parquet/src/file/statistics.rs
index 61edaa6129..21932fc303 100644
--- a/parquet/src/file/statistics.rs
+++ b/parquet/src/file/statistics.rs
@@ -167,19 +167,19 @@ pub(crate) fn from_thrift_page_stats(
             };
 
             fn check_len(min: &Option<Vec<u8>>, max: &Option<Vec<u8>>, len: 
usize) -> Result<()> {
-                if let Some(min) = min {
-                    if min.len() < len {
-                        return Err(ParquetError::General(
-                            "Insufficient bytes to parse min 
statistic".to_string(),
-                        ));
-                    }
+                if let Some(min) = min
+                    && min.len() < len
+                {
+                    return Err(ParquetError::General(
+                        "Insufficient bytes to parse min 
statistic".to_string(),
+                    ));
                 }
-                if let Some(max) = max {
-                    if max.len() < len {
-                        return Err(ParquetError::General(
-                            "Insufficient bytes to parse max 
statistic".to_string(),
-                        ));
-                    }
+                if let Some(max) = max
+                    && max.len() < len
+                {
+                    return Err(ParquetError::General(
+                        "Insufficient bytes to parse max 
statistic".to_string(),
+                    ));
                 }
                 Ok(())
             }
diff --git a/parquet/src/file/writer.rs b/parquet/src/file/writer.rs
index dbd1a48f70..19de205be5 100644
--- a/parquet/src/file/writer.rs
+++ b/parquet/src/file/writer.rs
@@ -1108,10 +1108,10 @@ impl<W: Write + Send> PageWriter for 
SerializedPageWriter<'_, W> {
         spec.bytes_written = self.sink.bytes_written() as u64 - start_pos;
         spec.num_values = page.num_values();
 
-        if let Some(page_encryptor) = self.page_encryptor_mut() {
-            if page.compressed_page().is_data_page() {
-                page_encryptor.increment_page();
-            }
+        if let Some(page_encryptor) = self.page_encryptor_mut()
+            && page.compressed_page().is_data_page()
+        {
+            page_encryptor.increment_page();
         }
         Ok(spec)
     }
diff --git a/parquet/src/schema/types.rs b/parquet/src/schema/types.rs
index 2b2c3f1bb0..784c61d002 100644
--- a/parquet/src/schema/types.rs
+++ b/parquet/src/schema/types.rs
@@ -1309,12 +1309,15 @@ fn build_tree<'a>(
 
 /// Checks if the logical type is valid.
 fn check_logical_type(logical_type: &Option<LogicalType>) -> Result<()> {
-    if let Some(LogicalType::Integer(IntType { bit_width, .. })) = 
logical_type {
-        if *bit_width != 8 && *bit_width != 16 && *bit_width != 32 && 
*bit_width != 64 {
-            return Err(general_err!(
-                "Bit width must be 8, 16, 32, or 64 for Integer logical type"
-            ));
-        }
+    if let Some(LogicalType::Integer(IntType { bit_width, .. })) = logical_type
+        && *bit_width != 8
+        && *bit_width != 16
+        && *bit_width != 32
+        && *bit_width != 64
+    {
+        return Err(general_err!(
+            "Bit width must be 8, 16, 32, or 64 for Integer logical type"
+        ));
     }
     Ok(())
 }
diff --git a/parquet/src/util/bit_util.rs b/parquet/src/util/bit_util.rs
index d5f5945a12..b8ee659678 100644
--- a/parquet/src/util/bit_util.rs
+++ b/parquet/src/util/bit_util.rs
@@ -1371,7 +1371,7 @@ mod tests {
         StandardUniform: Distribution<T>,
     {
         assert!(num_bits <= 32);
-        assert!(total % 2 == 0);
+        assert!(total.is_multiple_of(2));
 
         let aligned_value_byte_width = std::mem::size_of::<T>();
         let value_byte_width = ceil(num_bits, 8);
diff --git a/parquet_derive/src/parquet_field.rs 
b/parquet_derive/src/parquet_field.rs
index fd77247d1e..5438ef7fa5 100644
--- a/parquet_derive/src/parquet_field.rs
+++ b/parquet_derive/src/parquet_field.rs
@@ -619,17 +619,17 @@ impl Type {
 
         match leaf_type {
             Type::Array(first_type, _length) => {
-                if let Type::TypePath(_) = **first_type {
-                    if last_part == "u8" {
-                        return PhysicalType::FixedLenByteArray;
-                    }
+                if let Type::TypePath(_) = **first_type
+                    && last_part == "u8"
+                {
+                    return PhysicalType::FixedLenByteArray;
                 }
             }
             Type::Vec(first_type) | Type::Slice(first_type) => {
-                if let Type::TypePath(_) = **first_type {
-                    if last_part == "u8" {
-                        return PhysicalType::ByteArray;
-                    }
+                if let Type::TypePath(_) = **first_type
+                    && last_part == "u8"
+                {
+                    return PhysicalType::ByteArray;
                 }
             }
             _ => (),
@@ -660,12 +660,11 @@ impl Type {
         let leaf_type = self.leaf_type_recursive();
 
         // `[u8; N]` => Some(N)
-        if let Type::Array(first_type, length) = leaf_type {
-            if let Type::TypePath(_) = **first_type {
-                if last_part == "u8" {
-                    return Some(length.clone());
-                }
-            }
+        if let Type::Array(first_type, length) = leaf_type
+            && let Type::TypePath(_) = **first_type
+            && last_part == "u8"
+        {
+            return Some(length.clone());
         }
 
         match last_part.trim() {
@@ -681,17 +680,17 @@ impl Type {
 
         match leaf_type {
             Type::Array(first_type, _length) => {
-                if let Type::TypePath(_) = **first_type {
-                    if last_part == "u8" {
-                        return quote! { None };
-                    }
+                if let Type::TypePath(_) = **first_type
+                    && last_part == "u8"
+                {
+                    return quote! { None };
                 }
             }
             Type::Vec(first_type) | Type::Slice(first_type) => {
-                if let Type::TypePath(_) = **first_type {
-                    if last_part == "u8" {
-                        return quote! { None };
-                    }
+                if let Type::TypePath(_) = **first_type
+                    && last_part == "u8"
+                {
+                    return quote! { None };
                 }
             }
             _ => (),

Reply via email to