You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by uw...@apache.org on 2018/05/04 08:21:21 UTC

[arrow] branch master updated: ARROW-2478: [C++] Introduce a checked_cast function that performs a dynamic_cast in debug mode

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

uwe pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new 73f0d8e  ARROW-2478: [C++] Introduce a checked_cast function that performs a dynamic_cast in debug mode
73f0d8e is described below

commit 73f0d8e92f1e23f0e0c36910e96f25729d24f259
Author: Phillip Cloud <cp...@gmail.com>
AuthorDate: Fri May 4 10:21:14 2018 +0200

    ARROW-2478: [C++] Introduce a checked_cast function that performs a dynamic_cast in debug mode
    
    Author: Phillip Cloud <cp...@gmail.com>
    
    Closes #1937 from cpcloud/ARROW-2478 and squashes the following commits:
    
    afa88af2 <Phillip Cloud> ARROW-2478:  Introduce a checked_cast function that performs a dynamic_cast in debug mode
---
 cpp/src/arrow/adapters/orc/adapter.cc          | 47 ++++++++---------
 cpp/src/arrow/array-test.cc                    | 71 +++++++++++++-------------
 cpp/src/arrow/array.cc                         | 23 +++++----
 cpp/src/arrow/array.h                          |  5 +-
 cpp/src/arrow/builder.cc                       | 15 +++---
 cpp/src/arrow/compare.cc                       | 49 +++++++++---------
 cpp/src/arrow/compute/kernels/cast.cc          | 33 ++++++------
 cpp/src/arrow/compute/kernels/hash.cc          | 15 +++---
 cpp/src/arrow/io/io-memory-test.cc             |  3 +-
 cpp/src/arrow/ipc/feather-test.cc              |  3 +-
 cpp/src/arrow/ipc/feather.cc                   | 13 ++---
 cpp/src/arrow/ipc/ipc-read-write-test.cc       |  9 ++--
 cpp/src/arrow/ipc/json-internal.cc             | 11 ++--
 cpp/src/arrow/ipc/metadata-internal.cc         | 19 +++----
 cpp/src/arrow/ipc/writer.cc                    |  9 ++--
 cpp/src/arrow/pretty_print.cc                  |  3 +-
 cpp/src/arrow/python/arrow_to_pandas.cc        | 59 ++++++++++-----------
 cpp/src/arrow/python/arrow_to_python.cc        | 33 ++++++------
 cpp/src/arrow/python/builtin_convert.cc        | 31 ++++++-----
 cpp/src/arrow/python/helpers.cc                |  3 +-
 cpp/src/arrow/python/numpy_to_arrow.cc         | 25 ++++-----
 cpp/src/arrow/python/python-test.cc            | 13 ++---
 cpp/src/arrow/status.cc                        |  2 +-
 cpp/src/arrow/table_builder-test.cc            |  5 +-
 cpp/src/arrow/table_builder.h                  |  3 +-
 cpp/src/arrow/tensor.cc                        |  7 +--
 cpp/src/arrow/type-test.cc                     |  5 +-
 cpp/src/arrow/type.cc                          |  3 +-
 cpp/src/arrow/type.h                           |  3 +-
 cpp/src/arrow/util/CMakeLists.txt              |  1 +
 cpp/src/arrow/util/checked-cast-test.cc        | 71 ++++++++++++++++++++++++++
 cpp/src/arrow/util/{hash.cc => checked_cast.h} | 32 +++++++-----
 cpp/src/arrow/util/hash.cc                     |  2 +-
 cpp/src/arrow/visitor_inline.h                 |  5 +-
 python/pyarrow/tests/pandas_examples.py        |  4 +-
 35 files changed, 371 insertions(+), 264 deletions(-)

diff --git a/cpp/src/arrow/adapters/orc/adapter.cc b/cpp/src/arrow/adapters/orc/adapter.cc
index 527b9e9..7350f79 100644
--- a/cpp/src/arrow/adapters/orc/adapter.cc
+++ b/cpp/src/arrow/adapters/orc/adapter.cc
@@ -37,6 +37,7 @@
 #include "arrow/type.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/macros.h"
 #include "arrow/util/visibility.h"
@@ -360,7 +361,7 @@ class ORCFileReader::Impl {
     RETURN_NOT_OK(RecordBatchBuilder::Make(schema, pool_, nrows, &builder));
 
     // The top-level type must be a struct to read into an arrow table
-    const auto& struct_batch = static_cast<liborc::StructVectorBatch&>(*batch);
+    const auto& struct_batch = checked_cast<liborc::StructVectorBatch&>(*batch);
 
     while (rowreader->next(*batch)) {
       for (int i = 0; i < builder->num_fields(); i++) {
@@ -428,8 +429,8 @@ class ORCFileReader::Impl {
 
   Status AppendStructBatch(const liborc::Type* type, liborc::ColumnVectorBatch* cbatch,
                            int64_t offset, int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<StructBuilder*>(abuilder);
-    auto batch = static_cast<liborc::StructVectorBatch*>(cbatch);
+    auto builder = checked_cast<StructBuilder*>(abuilder);
+    auto batch = checked_cast<liborc::StructVectorBatch*>(cbatch);
 
     const uint8_t* valid_bytes = nullptr;
     if (batch->hasNulls) {
@@ -446,8 +447,8 @@ class ORCFileReader::Impl {
 
   Status AppendListBatch(const liborc::Type* type, liborc::ColumnVectorBatch* cbatch,
                          int64_t offset, int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<ListBuilder*>(abuilder);
-    auto batch = static_cast<liborc::ListVectorBatch*>(cbatch);
+    auto builder = checked_cast<ListBuilder*>(abuilder);
+    auto batch = checked_cast<liborc::ListVectorBatch*>(cbatch);
     liborc::ColumnVectorBatch* elements = batch->elements.get();
     const liborc::Type* elemtype = type->getSubtype(0);
 
@@ -468,9 +469,9 @@ class ORCFileReader::Impl {
 
   Status AppendMapBatch(const liborc::Type* type, liborc::ColumnVectorBatch* cbatch,
                         int64_t offset, int64_t length, ArrayBuilder* abuilder) {
-    auto list_builder = static_cast<ListBuilder*>(abuilder);
-    auto struct_builder = static_cast<StructBuilder*>(list_builder->value_builder());
-    auto batch = static_cast<liborc::MapVectorBatch*>(cbatch);
+    auto list_builder = checked_cast<ListBuilder*>(abuilder);
+    auto struct_builder = checked_cast<StructBuilder*>(list_builder->value_builder());
+    auto batch = checked_cast<liborc::MapVectorBatch*>(cbatch);
     liborc::ColumnVectorBatch* keys = batch->keys.get();
     liborc::ColumnVectorBatch* vals = batch->elements.get();
     const liborc::Type* keytype = type->getSubtype(0);
@@ -495,8 +496,8 @@ class ORCFileReader::Impl {
   template <class builder_type, class batch_type, class elem_type>
   Status AppendNumericBatch(liborc::ColumnVectorBatch* cbatch, int64_t offset,
                             int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<builder_type*>(abuilder);
-    auto batch = static_cast<batch_type*>(cbatch);
+    auto builder = checked_cast<builder_type*>(abuilder);
+    auto batch = checked_cast<batch_type*>(cbatch);
 
     if (length == 0) {
       return Status::OK();
@@ -513,8 +514,8 @@ class ORCFileReader::Impl {
   template <class builder_type, class target_type, class batch_type, class source_type>
   Status AppendNumericBatchCast(liborc::ColumnVectorBatch* cbatch, int64_t offset,
                                 int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<builder_type*>(abuilder);
-    auto batch = static_cast<batch_type*>(cbatch);
+    auto builder = checked_cast<builder_type*>(abuilder);
+    auto batch = checked_cast<batch_type*>(cbatch);
 
     if (length == 0) {
       return Status::OK();
@@ -537,8 +538,8 @@ class ORCFileReader::Impl {
 
   Status AppendBoolBatch(liborc::ColumnVectorBatch* cbatch, int64_t offset,
                          int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<BooleanBuilder*>(abuilder);
-    auto batch = static_cast<liborc::LongVectorBatch*>(cbatch);
+    auto builder = checked_cast<BooleanBuilder*>(abuilder);
+    auto batch = checked_cast<liborc::LongVectorBatch*>(cbatch);
 
     if (length == 0) {
       return Status::OK();
@@ -566,8 +567,8 @@ class ORCFileReader::Impl {
 
   Status AppendTimestampBatch(liborc::ColumnVectorBatch* cbatch, int64_t offset,
                               int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<TimestampBuilder*>(abuilder);
-    auto batch = static_cast<liborc::TimestampVectorBatch*>(cbatch);
+    auto builder = checked_cast<TimestampBuilder*>(abuilder);
+    auto batch = checked_cast<liborc::TimestampVectorBatch*>(cbatch);
 
     if (length == 0) {
       return Status::OK();
@@ -595,8 +596,8 @@ class ORCFileReader::Impl {
   template <class builder_type>
   Status AppendBinaryBatch(liborc::ColumnVectorBatch* cbatch, int64_t offset,
                            int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<builder_type*>(abuilder);
-    auto batch = static_cast<liborc::StringVectorBatch*>(cbatch);
+    auto builder = checked_cast<builder_type*>(abuilder);
+    auto batch = checked_cast<liborc::StringVectorBatch*>(cbatch);
 
     const bool has_nulls = batch->hasNulls;
     for (int64_t i = offset; i < length + offset; i++) {
@@ -612,8 +613,8 @@ class ORCFileReader::Impl {
 
   Status AppendFixedBinaryBatch(liborc::ColumnVectorBatch* cbatch, int64_t offset,
                                 int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<FixedSizeBinaryBuilder*>(abuilder);
-    auto batch = static_cast<liborc::StringVectorBatch*>(cbatch);
+    auto builder = checked_cast<FixedSizeBinaryBuilder*>(abuilder);
+    auto batch = checked_cast<liborc::StringVectorBatch*>(cbatch);
 
     const bool has_nulls = batch->hasNulls;
     for (int64_t i = offset; i < length + offset; i++) {
@@ -628,11 +629,11 @@ class ORCFileReader::Impl {
 
   Status AppendDecimalBatch(const liborc::Type* type, liborc::ColumnVectorBatch* cbatch,
                             int64_t offset, int64_t length, ArrayBuilder* abuilder) {
-    auto builder = static_cast<Decimal128Builder*>(abuilder);
+    auto builder = checked_cast<Decimal128Builder*>(abuilder);
 
     const bool has_nulls = cbatch->hasNulls;
     if (type->getPrecision() == 0 || type->getPrecision() > 18) {
-      auto batch = static_cast<liborc::Decimal128VectorBatch*>(cbatch);
+      auto batch = checked_cast<liborc::Decimal128VectorBatch*>(cbatch);
       for (int64_t i = offset; i < length + offset; i++) {
         if (!has_nulls || batch->notNull[i]) {
           RETURN_NOT_OK(builder->Append(
@@ -642,7 +643,7 @@ class ORCFileReader::Impl {
         }
       }
     } else {
-      auto batch = static_cast<liborc::Decimal64VectorBatch*>(cbatch);
+      auto batch = checked_cast<liborc::Decimal64VectorBatch*>(cbatch);
       for (int64_t i = offset; i < length + offset; i++) {
         if (!has_nulls || batch->notNull[i]) {
           RETURN_NOT_OK(builder->Append(Decimal128(batch->values[i])));
diff --git a/cpp/src/arrow/array-test.cc b/cpp/src/arrow/array-test.cc
index 60ed291..0095832 100644
--- a/cpp/src/arrow/array-test.cc
+++ b/cpp/src/arrow/array-test.cc
@@ -33,6 +33,7 @@
 #include "arrow/test-util.h"
 #include "arrow/type.h"
 #include "arrow/type_traits.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 
 namespace arrow {
@@ -223,10 +224,10 @@ class TestPrimitiveBuilder : public TestBuilder {
 
     std::unique_ptr<ArrayBuilder> tmp;
     ASSERT_OK(MakeBuilder(pool_, type_, &tmp));
-    builder_.reset(static_cast<BuilderType*>(tmp.release()));
+    builder_.reset(checked_cast<BuilderType*>(tmp.release()));
 
     ASSERT_OK(MakeBuilder(pool_, type_, &tmp));
-    builder_nn_.reset(static_cast<BuilderType*>(tmp.release()));
+    builder_nn_.reset(checked_cast<BuilderType*>(tmp.release()));
   }
 
   void RandomData(int64_t N, double pct_null = 0.1) {
@@ -733,8 +734,8 @@ TEST(TestBooleanBuilder, TestStdBoolVectorAppend) {
   ASSERT_OK(builder.Finish(&result));
   ASSERT_OK(builder_nn.Finish(&result_nn));
 
-  const auto& arr = static_cast<const BooleanArray&>(*result);
-  const auto& arr_nn = static_cast<const BooleanArray&>(*result_nn);
+  const auto& arr = checked_cast<const BooleanArray&>(*result);
+  const auto& arr_nn = checked_cast<const BooleanArray&>(*result_nn);
   for (int i = 0; i < length; ++i) {
     if (is_valid[i]) {
       ASSERT_FALSE(arr.IsNull(i));
@@ -897,9 +898,9 @@ TEST_F(TestStringArray, CompareNullByteSlots) {
   ASSERT_OK(builder2.Finish(&array2));
   ASSERT_OK(builder3.Finish(&array3));
 
-  const auto& a1 = static_cast<const StringArray&>(*array);
-  const auto& a2 = static_cast<const StringArray&>(*array2);
-  const auto& a3 = static_cast<const StringArray&>(*array3);
+  const auto& a1 = checked_cast<const StringArray&>(*array);
+  const auto& a2 = checked_cast<const StringArray&>(*array2);
+  const auto& a3 = checked_cast<const StringArray&>(*array3);
 
   // The validity bitmaps are the same, the data is different, but the unequal
   // portion is masked out
@@ -1198,7 +1199,7 @@ TEST_F(TestBinaryArray, TestEqualsEmptyStrings) {
   std::shared_ptr<Array> left_arr;
   ASSERT_OK(builder.Finish(&left_arr));
 
-  const BinaryArray& left = static_cast<const BinaryArray&>(*left_arr);
+  const BinaryArray& left = checked_cast<const BinaryArray&>(*left_arr);
   std::shared_ptr<Array> right =
       std::make_shared<BinaryArray>(left.length(), left.value_offsets(), nullptr,
                                     left.null_bitmap(), left.null_count());
@@ -1396,7 +1397,7 @@ TEST_F(TestFWBinaryArray, Builder) {
 
   auto CheckResult = [&length, &is_valid, &raw_data, &byte_width](const Array& result) {
     // Verify output
-    const auto& fw_result = static_cast<const FixedSizeBinaryArray&>(result);
+    const auto& fw_result = checked_cast<const FixedSizeBinaryArray&>(result);
 
     ASSERT_EQ(length, result.length());
 
@@ -1466,8 +1467,8 @@ TEST_F(TestFWBinaryArray, EqualsRangeEquals) {
   ASSERT_OK(builder1.Finish(&array1));
   ASSERT_OK(builder2.Finish(&array2));
 
-  const auto& a1 = static_cast<const FixedSizeBinaryArray&>(*array1);
-  const auto& a2 = static_cast<const FixedSizeBinaryArray&>(*array2);
+  const auto& a1 = checked_cast<const FixedSizeBinaryArray&>(*array1);
+  const auto& a2 = checked_cast<const FixedSizeBinaryArray&>(*array2);
 
   FixedSizeBinaryArray equal1(type, 2, a1.values(), a1.null_bitmap(), 1);
   FixedSizeBinaryArray equal2(type, 2, a2.values(), a1.null_bitmap(), 1);
@@ -1490,7 +1491,7 @@ TEST_F(TestFWBinaryArray, ZeroSize) {
   std::shared_ptr<Array> array;
   ASSERT_OK(builder.Finish(&array));
 
-  const auto& fw_array = static_cast<const FixedSizeBinaryArray&>(*array);
+  const auto& fw_array = checked_cast<const FixedSizeBinaryArray&>(*array);
 
   // data is never allocated
   ASSERT_TRUE(fw_array.values() == nullptr);
@@ -2473,7 +2474,7 @@ class TestListArray : public TestBuilder {
 
     std::unique_ptr<ArrayBuilder> tmp;
     ASSERT_OK(MakeBuilder(pool_, type_, &tmp));
-    builder_.reset(static_cast<ListBuilder*>(tmp.release()));
+    builder_.reset(checked_cast<ListBuilder*>(tmp.release()));
   }
 
   void Done() {
@@ -2490,7 +2491,7 @@ class TestListArray : public TestBuilder {
 };
 
 TEST_F(TestListArray, Equality) {
-  Int32Builder* vb = static_cast<Int32Builder*>(builder_->value_builder());
+  Int32Builder* vb = checked_cast<Int32Builder*>(builder_->value_builder());
 
   std::shared_ptr<Array> array, equal_array, unequal_array;
   vector<int32_t> equal_offsets = {0, 1, 2, 5, 6, 7, 8, 10};
@@ -2654,7 +2655,7 @@ TEST_F(TestListArray, TestBasics) {
   vector<int> lengths = {3, 0, 4};
   vector<uint8_t> is_valid = {1, 0, 1};
 
-  Int32Builder* vb = static_cast<Int32Builder*>(builder_->value_builder());
+  Int32Builder* vb = checked_cast<Int32Builder*>(builder_->value_builder());
 
   ASSERT_OK(builder_->Reserve(lengths.size()));
   ASSERT_OK(vb->Reserve(values.size()));
@@ -2677,7 +2678,7 @@ TEST_F(TestListArray, BulkAppend) {
   vector<uint8_t> is_valid = {1, 0, 1};
   vector<int32_t> offsets = {0, 3, 3};
 
-  Int32Builder* vb = static_cast<Int32Builder*>(builder_->value_builder());
+  Int32Builder* vb = checked_cast<Int32Builder*>(builder_->value_builder());
   ASSERT_OK(vb->Reserve(values.size()));
 
   ASSERT_OK(builder_->AppendValues(offsets.data(), offsets.size(), is_valid.data()));
@@ -2695,7 +2696,7 @@ TEST_F(TestListArray, BulkAppendInvalid) {
   vector<uint8_t> is_valid = {1, 0, 1};
   vector<int32_t> offsets = {0, 2, 4};  // should be 0, 3, 3 given the is_null array
 
-  Int32Builder* vb = static_cast<Int32Builder*>(builder_->value_builder());
+  Int32Builder* vb = checked_cast<Int32Builder*>(builder_->value_builder());
   ASSERT_OK(vb->Reserve(values.size()));
 
   ASSERT_OK(builder_->AppendValues(offsets.data(), offsets.size(), is_valid.data()));
@@ -2928,7 +2929,7 @@ class TestStructBuilder : public TestBuilder {
 
     std::unique_ptr<ArrayBuilder> tmp;
     ASSERT_OK(MakeBuilder(pool_, type_, &tmp));
-    builder_.reset(static_cast<StructBuilder*>(tmp.release()));
+    builder_.reset(checked_cast<StructBuilder*>(tmp.release()));
     ASSERT_EQ(2, static_cast<int>(builder_->num_fields()));
   }
 
@@ -2950,12 +2951,12 @@ TEST_F(TestStructBuilder, TestAppendNull) {
   ASSERT_OK(builder_->AppendNull());
   ASSERT_EQ(2, static_cast<int>(builder_->num_fields()));
 
-  ListBuilder* list_vb = static_cast<ListBuilder*>(builder_->field_builder(0));
+  ListBuilder* list_vb = checked_cast<ListBuilder*>(builder_->field_builder(0));
   ASSERT_OK(list_vb->AppendNull());
   ASSERT_OK(list_vb->AppendNull());
   ASSERT_EQ(2, list_vb->length());
 
-  Int32Builder* int_vb = static_cast<Int32Builder*>(builder_->field_builder(1));
+  Int32Builder* int_vb = checked_cast<Int32Builder*>(builder_->field_builder(1));
   ASSERT_OK(int_vb->AppendNull());
   ASSERT_OK(int_vb->AppendNull());
   ASSERT_EQ(2, int_vb->length());
@@ -2987,9 +2988,9 @@ TEST_F(TestStructBuilder, TestBasics) {
   vector<uint8_t> list_is_valid = {1, 0, 1, 1};
   vector<uint8_t> struct_is_valid = {1, 1, 1, 1};
 
-  ListBuilder* list_vb = static_cast<ListBuilder*>(builder_->field_builder(0));
-  Int8Builder* char_vb = static_cast<Int8Builder*>(list_vb->value_builder());
-  Int32Builder* int_vb = static_cast<Int32Builder*>(builder_->field_builder(1));
+  ListBuilder* list_vb = checked_cast<ListBuilder*>(builder_->field_builder(0));
+  Int8Builder* char_vb = checked_cast<Int8Builder*>(list_vb->value_builder());
+  Int32Builder* int_vb = checked_cast<Int32Builder*>(builder_->field_builder(1));
   ASSERT_EQ(2, static_cast<int>(builder_->num_fields()));
 
   EXPECT_OK(builder_->Resize(list_lengths.size()));
@@ -3023,9 +3024,9 @@ TEST_F(TestStructBuilder, BulkAppend) {
   vector<uint8_t> list_is_valid = {1, 0, 1, 1};
   vector<uint8_t> struct_is_valid = {1, 1, 1, 1};
 
-  ListBuilder* list_vb = static_cast<ListBuilder*>(builder_->field_builder(0));
-  Int8Builder* char_vb = static_cast<Int8Builder*>(list_vb->value_builder());
-  Int32Builder* int_vb = static_cast<Int32Builder*>(builder_->field_builder(1));
+  ListBuilder* list_vb = checked_cast<ListBuilder*>(builder_->field_builder(0));
+  Int8Builder* char_vb = checked_cast<Int8Builder*>(list_vb->value_builder());
+  Int32Builder* int_vb = checked_cast<Int32Builder*>(builder_->field_builder(1));
 
   ASSERT_OK(builder_->Resize(list_lengths.size()));
   ASSERT_OK(char_vb->Resize(list_values.size()));
@@ -3055,9 +3056,9 @@ TEST_F(TestStructBuilder, BulkAppendInvalid) {
   vector<uint8_t> list_is_valid = {1, 0, 1, 1};
   vector<uint8_t> struct_is_valid = {1, 0, 1, 1};  // should be 1, 1, 1, 1
 
-  ListBuilder* list_vb = static_cast<ListBuilder*>(builder_->field_builder(0));
-  Int8Builder* char_vb = static_cast<Int8Builder*>(list_vb->value_builder());
-  Int32Builder* int_vb = static_cast<Int32Builder*>(builder_->field_builder(1));
+  ListBuilder* list_vb = checked_cast<ListBuilder*>(builder_->field_builder(0));
+  Int8Builder* char_vb = checked_cast<Int8Builder*>(list_vb->value_builder());
+  Int32Builder* int_vb = checked_cast<Int32Builder*>(builder_->field_builder(1));
 
   ASSERT_OK(builder_->Reserve(list_lengths.size()));
   ASSERT_OK(char_vb->Reserve(list_values.size()));
@@ -3097,9 +3098,9 @@ TEST_F(TestStructBuilder, TestEquality) {
   vector<uint8_t> unequal_list_is_valid = {1, 1, 1, 1};
   vector<uint8_t> unequal_struct_is_valid = {1, 0, 0, 1};
 
-  ListBuilder* list_vb = static_cast<ListBuilder*>(builder_->field_builder(0));
-  Int8Builder* char_vb = static_cast<Int8Builder*>(list_vb->value_builder());
-  Int32Builder* int_vb = static_cast<Int32Builder*>(builder_->field_builder(1));
+  ListBuilder* list_vb = checked_cast<ListBuilder*>(builder_->field_builder(0));
+  Int8Builder* char_vb = checked_cast<Int8Builder*>(list_vb->value_builder());
+  Int32Builder* int_vb = checked_cast<Int32Builder*>(builder_->field_builder(1));
   ASSERT_OK(builder_->Reserve(list_lengths.size()));
   ASSERT_OK(char_vb->Reserve(list_values.size()));
   ASSERT_OK(int_vb->Reserve(int_values.size()));
@@ -3226,9 +3227,9 @@ TEST_F(TestStructBuilder, TestSlice) {
   vector<uint8_t> list_is_valid = {1, 0, 1, 1};
   vector<uint8_t> struct_is_valid = {1, 1, 1, 1};
 
-  ListBuilder* list_vb = static_cast<ListBuilder*>(builder_->field_builder(0));
-  Int8Builder* char_vb = static_cast<Int8Builder*>(list_vb->value_builder());
-  Int32Builder* int_vb = static_cast<Int32Builder*>(builder_->field_builder(1));
+  ListBuilder* list_vb = checked_cast<ListBuilder*>(builder_->field_builder(0));
+  Int8Builder* char_vb = checked_cast<Int8Builder*>(list_vb->value_builder());
+  Int32Builder* int_vb = checked_cast<Int32Builder*>(builder_->field_builder(1));
   ASSERT_OK(builder_->Reserve(list_lengths.size()));
   ASSERT_OK(char_vb->Reserve(list_values.size()));
   ASSERT_OK(int_vb->Reserve(int_values.size()));
diff --git a/cpp/src/arrow/array.cc b/cpp/src/arrow/array.cc
index e854114..fb6ccaf 100644
--- a/cpp/src/arrow/array.cc
+++ b/cpp/src/arrow/array.cc
@@ -31,6 +31,7 @@
 #include "arrow/status.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/macros.h"
@@ -208,7 +209,7 @@ Status ListArray::FromArrays(const Array& offsets, const Array& values, MemoryPo
 
   BufferVector buffers = {};
 
-  const auto& typed_offsets = static_cast<const Int32Array&>(offsets);
+  const auto& typed_offsets = checked_cast<const Int32Array&>(offsets);
 
   const int64_t num_offsets = offsets.length();
 
@@ -265,7 +266,7 @@ void ListArray::SetData(const std::shared_ptr<ArrayData>& data) {
 }
 
 std::shared_ptr<DataType> ListArray::value_type() const {
-  return static_cast<const ListType&>(*type()).value_type();
+  return checked_cast<const ListType&>(*type()).value_type();
 }
 
 std::shared_ptr<Array> ListArray::values() const { return values_; }
@@ -329,7 +330,7 @@ FixedSizeBinaryArray::FixedSizeBinaryArray(const std::shared_ptr<DataType>& type
                                            const std::shared_ptr<Buffer>& null_bitmap,
                                            int64_t null_count, int64_t offset)
     : PrimitiveArray(type, length, data, null_bitmap, null_count, offset),
-      byte_width_(static_cast<const FixedSizeBinaryType&>(*type).byte_width()) {}
+      byte_width_(checked_cast<const FixedSizeBinaryType&>(*type).byte_width()) {}
 
 const uint8_t* FixedSizeBinaryArray::GetValue(int64_t i) const {
   return raw_values_ + (i + data_->offset) * byte_width_;
@@ -344,7 +345,7 @@ Decimal128Array::Decimal128Array(const std::shared_ptr<ArrayData>& data)
 }
 
 std::string Decimal128Array::FormatValue(int64_t i) const {
-  const auto& type_ = static_cast<const Decimal128Type&>(*type());
+  const auto& type_ = checked_cast<const Decimal128Type&>(*type());
   const Decimal128 value(GetValue(i));
   return value.ToString(type_.scale());
 }
@@ -487,8 +488,8 @@ Status UnionArray::MakeDense(const Array& type_ids, const Array& value_offsets,
   }
 
   BufferVector buffers = {type_ids.null_bitmap(),
-                          static_cast<const UInt8Array&>(type_ids).values(),
-                          static_cast<const Int32Array&>(value_offsets).values()};
+                          checked_cast<const Int8Array&>(type_ids).values(),
+                          checked_cast<const Int32Array&>(value_offsets).values()};
   auto union_type = union_(children, UnionMode::DENSE);
   auto internal_data = ArrayData::Make(union_type, type_ids.length(), std::move(buffers),
                                        type_ids.null_count(), type_ids.offset());
@@ -506,7 +507,7 @@ Status UnionArray::MakeSparse(const Array& type_ids,
     return Status::Invalid("UnionArray type_ids must be signed int8");
   }
   BufferVector buffers = {type_ids.null_bitmap(),
-                          static_cast<const UInt8Array&>(type_ids).values(), nullptr};
+                          checked_cast<const Int8Array&>(type_ids).values(), nullptr};
   auto union_type = union_(children, UnionMode::SPARSE);
   auto internal_data = ArrayData::Make(union_type, type_ids.length(), std::move(buffers),
                                        type_ids.null_count(), type_ids.offset());
@@ -559,7 +560,7 @@ template <typename ArrowType>
 Status ValidateDictionaryIndices(const std::shared_ptr<Array>& indices,
                                  const int64_t upper_bound) {
   using ArrayType = typename TypeTraits<ArrowType>::ArrayType;
-  const auto& array = static_cast<const ArrayType&>(*indices);
+  const auto& array = checked_cast<const ArrayType&>(*indices);
   const typename ArrowType::c_type* data = array.raw_values();
   const int64_t size = array.length();
 
@@ -583,14 +584,14 @@ Status ValidateDictionaryIndices(const std::shared_ptr<Array>& indices,
 }
 
 DictionaryArray::DictionaryArray(const std::shared_ptr<ArrayData>& data)
-    : dict_type_(static_cast<const DictionaryType*>(data->type.get())) {
+    : dict_type_(checked_cast<const DictionaryType*>(data->type.get())) {
   DCHECK_EQ(data->type->id(), Type::DICTIONARY);
   SetData(data);
 }
 
 DictionaryArray::DictionaryArray(const std::shared_ptr<DataType>& type,
                                  const std::shared_ptr<Array>& indices)
-    : dict_type_(static_cast<const DictionaryType*>(type.get())) {
+    : dict_type_(checked_cast<const DictionaryType*>(type.get())) {
   DCHECK_EQ(type->id(), Type::DICTIONARY);
   DCHECK_EQ(indices->type_id(), dict_type_->index_type()->id());
   auto data = indices->data()->Copy();
@@ -602,7 +603,7 @@ Status DictionaryArray::FromArrays(const std::shared_ptr<DataType>& type,
                                    const std::shared_ptr<Array>& indices,
                                    std::shared_ptr<Array>* out) {
   DCHECK_EQ(type->id(), Type::DICTIONARY);
-  const auto& dict = static_cast<const DictionaryType&>(*type);
+  const auto& dict = checked_cast<const DictionaryType&>(*type);
   DCHECK_EQ(indices->type_id(), dict.index_type()->id());
 
   int64_t upper_bound = dict.dictionary()->length();
diff --git a/cpp/src/arrow/array.h b/cpp/src/arrow/array.h
index 1b152c4..6f61511 100644
--- a/cpp/src/arrow/array.h
+++ b/cpp/src/arrow/array.h
@@ -30,6 +30,7 @@
 #include "arrow/type_fwd.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/macros.h"
 #include "arrow/util/visibility.h"
 #include "arrow/visitor.h"
@@ -583,7 +584,7 @@ class ARROW_EXPORT FixedSizeBinaryArray : public PrimitiveArray {
  protected:
   inline void SetData(const std::shared_ptr<ArrayData>& data) {
     this->PrimitiveArray::SetData(data);
-    byte_width_ = static_cast<const FixedSizeBinaryType&>(*type()).byte_width();
+    byte_width_ = checked_cast<const FixedSizeBinaryType&>(*type()).byte_width();
   }
 
   int32_t byte_width_;
@@ -693,7 +694,7 @@ class ARROW_EXPORT UnionArray : public Array {
   const type_id_t* raw_type_ids() const { return raw_type_ids_ + data_->offset; }
   const int32_t* raw_value_offsets() const { return raw_value_offsets_ + data_->offset; }
 
-  UnionMode::type mode() const { return static_cast<const UnionType&>(*type()).mode(); }
+  UnionMode::type mode() const { return checked_cast<const UnionType&>(*type()).mode(); }
 
   // Return the given field as an individual array.
   // For sparse unions, the returned array has its offset, length and null
diff --git a/cpp/src/arrow/builder.cc b/cpp/src/arrow/builder.cc
index 71af87a..39363fd 100644
--- a/cpp/src/arrow/builder.cc
+++ b/cpp/src/arrow/builder.cc
@@ -32,6 +32,7 @@
 #include "arrow/type.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/cpu-info.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/hash-util.h"
@@ -905,7 +906,7 @@ DictionaryBuilder<FixedSizeBinaryType>::DictionaryBuilder(
       dict_builder_(type, pool),
       overflow_dict_builder_(type, pool),
       values_builder_(pool),
-      byte_width_(static_cast<const FixedSizeBinaryType&>(*type).byte_width()) {
+      byte_width_(checked_cast<const FixedSizeBinaryType&>(*type).byte_width()) {
   if (!::arrow::CpuInfo::initialized()) {
     ::arrow::CpuInfo::Init();
   }
@@ -993,7 +994,7 @@ Status DictionaryBuilder<T>::Append(const Scalar& value) {
 
 template <typename T>
 Status DictionaryBuilder<T>::AppendArray(const Array& array) {
-  const auto& numeric_array = static_cast<const NumericArray<T>&>(array);
+  const auto& numeric_array = checked_cast<const NumericArray<T>&>(array);
   for (int64_t i = 0; i < array.length(); i++) {
     if (array.IsNull(i)) {
       RETURN_NOT_OK(AppendNull());
@@ -1017,7 +1018,7 @@ Status DictionaryBuilder<FixedSizeBinaryType>::AppendArray(const Array& array) {
     return Status::Invalid("Cannot append FixedSizeBinary array with non-matching type");
   }
 
-  const auto& numeric_array = static_cast<const FixedSizeBinaryArray&>(array);
+  const auto& numeric_array = checked_cast<const FixedSizeBinaryArray&>(array);
   for (int64_t i = 0; i < array.length(); i++) {
     if (array.IsNull(i)) {
       RETURN_NOT_OK(AppendNull());
@@ -1133,7 +1134,7 @@ bool DictionaryBuilder<T>::SlotDifferent(hash_slot_t index, const Scalar& value)
 template <>
 bool DictionaryBuilder<FixedSizeBinaryType>::SlotDifferent(hash_slot_t index,
                                                            const Scalar& value) {
-  int32_t width = static_cast<const FixedSizeBinaryType&>(*type_).byte_width();
+  int32_t width = checked_cast<const FixedSizeBinaryType&>(*type_).byte_width();
   bool value_found = false;
   if (index >= entry_id_offset_) {
     const Scalar other =
@@ -1172,7 +1173,7 @@ Status DictionaryBuilder<T>::AppendDictionary(const Scalar& value) {
                                                                                        \
   template <>                                                                          \
   Status DictionaryBuilder<Type>::AppendArray(const Array& array) {                    \
-    const BinaryArray& binary_array = static_cast<const BinaryArray&>(array);          \
+    const BinaryArray& binary_array = checked_cast<const BinaryArray&>(array);         \
     WrappedBinary value(nullptr, 0);                                                   \
     for (int64_t i = 0; i < array.length(); i++) {                                     \
       if (array.IsNull(i)) {                                                           \
@@ -1553,7 +1554,7 @@ Status StringBuilder::Append(const char** values, int64_t length,
 FixedSizeBinaryBuilder::FixedSizeBinaryBuilder(const std::shared_ptr<DataType>& type,
                                                MemoryPool* pool)
     : ArrayBuilder(type, pool),
-      byte_width_(static_cast<const FixedSizeBinaryType&>(*type).byte_width()),
+      byte_width_(checked_cast<const FixedSizeBinaryType&>(*type).byte_width()),
       byte_builder_(pool) {}
 
 Status FixedSizeBinaryBuilder::AppendValues(const uint8_t* data, int64_t length,
@@ -1669,7 +1670,7 @@ Status MakeBuilder(MemoryPool* pool, const std::shared_ptr<DataType>& type,
     case Type::LIST: {
       std::unique_ptr<ArrayBuilder> value_builder;
       std::shared_ptr<DataType> value_type =
-          static_cast<ListType*>(type.get())->value_type();
+          checked_cast<const ListType&>(*type).value_type();
       RETURN_NOT_OK(MakeBuilder(pool, value_type, &value_builder));
       out->reset(new ListBuilder(pool, std::move(value_builder)));
       return Status::OK();
diff --git a/cpp/src/arrow/compare.cc b/cpp/src/arrow/compare.cc
index b7b9313..51d8aee 100644
--- a/cpp/src/arrow/compare.cc
+++ b/cpp/src/arrow/compare.cc
@@ -29,6 +29,7 @@
 #include "arrow/type.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 #include "arrow/visitor_inline.h"
 
@@ -51,7 +52,7 @@ class RangeEqualsVisitor {
 
   template <typename ArrayType>
   inline Status CompareValues(const ArrayType& left) {
-    const auto& right = static_cast<const ArrayType&>(right_);
+    const auto& right = checked_cast<const ArrayType&>(right_);
 
     for (int64_t i = left_start_idx_, o_i = right_start_idx_; i < left_end_idx_;
          ++i, ++o_i) {
@@ -67,7 +68,7 @@ class RangeEqualsVisitor {
   }
 
   bool CompareBinaryRange(const BinaryArray& left) const {
-    const auto& right = static_cast<const BinaryArray&>(right_);
+    const auto& right = checked_cast<const BinaryArray&>(right_);
 
     for (int64_t i = left_start_idx_, o_i = right_start_idx_; i < left_end_idx_;
          ++i, ++o_i) {
@@ -96,7 +97,7 @@ class RangeEqualsVisitor {
   }
 
   bool CompareLists(const ListArray& left) {
-    const auto& right = static_cast<const ListArray&>(right_);
+    const auto& right = checked_cast<const ListArray&>(right_);
 
     const std::shared_ptr<Array>& left_values = left.values();
     const std::shared_ptr<Array>& right_values = right.values();
@@ -125,7 +126,7 @@ class RangeEqualsVisitor {
   }
 
   bool CompareStructs(const StructArray& left) {
-    const auto& right = static_cast<const StructArray&>(right_);
+    const auto& right = checked_cast<const StructArray&>(right_);
     bool equal_fields = true;
     for (int64_t i = left_start_idx_, o_i = right_start_idx_; i < left_end_idx_;
          ++i, ++o_i) {
@@ -146,14 +147,14 @@ class RangeEqualsVisitor {
   }
 
   bool CompareUnions(const UnionArray& left) const {
-    const auto& right = static_cast<const UnionArray&>(right_);
+    const auto& right = checked_cast<const UnionArray&>(right_);
 
     const UnionMode::type union_mode = left.mode();
     if (union_mode != right.mode()) {
       return false;
     }
 
-    const auto& left_type = static_cast<const UnionType&>(*left.type());
+    const auto& left_type = checked_cast<const UnionType&>(*left.type());
 
     // Define a mapping from the type id to child number
     uint8_t max_code = 0;
@@ -213,7 +214,7 @@ class RangeEqualsVisitor {
   }
 
   Status Visit(const FixedSizeBinaryArray& left) {
-    const auto& right = static_cast<const FixedSizeBinaryArray&>(right_);
+    const auto& right = checked_cast<const FixedSizeBinaryArray&>(right_);
 
     int32_t width = left.byte_width();
 
@@ -247,7 +248,7 @@ class RangeEqualsVisitor {
   }
 
   Status Visit(const Decimal128Array& left) {
-    return Visit(static_cast<const FixedSizeBinaryArray&>(left));
+    return Visit(checked_cast<const FixedSizeBinaryArray&>(left));
   }
 
   Status Visit(const NullArray& left) {
@@ -278,7 +279,7 @@ class RangeEqualsVisitor {
   }
 
   Status Visit(const DictionaryArray& left) {
-    const auto& right = static_cast<const DictionaryArray&>(right_);
+    const auto& right = checked_cast<const DictionaryArray&>(right_);
     if (!left.dictionary()->Equals(right.dictionary())) {
       result_ = false;
       return Status::OK();
@@ -343,7 +344,7 @@ class ArrayEqualsVisitor : public RangeEqualsVisitor {
   }
 
   Status Visit(const BooleanArray& left) {
-    const auto& right = static_cast<const BooleanArray&>(right_);
+    const auto& right = checked_cast<const BooleanArray&>(right_);
 
     if (left.null_count() > 0) {
       const uint8_t* left_data = left.values()->data();
@@ -369,13 +370,13 @@ class ArrayEqualsVisitor : public RangeEqualsVisitor {
                               !std::is_base_of<BooleanArray, T>::value,
                           Status>::type
   Visit(const T& left) {
-    result_ = IsEqualPrimitive(left, static_cast<const PrimitiveArray&>(right_));
+    result_ = IsEqualPrimitive(left, checked_cast<const PrimitiveArray&>(right_));
     return Status::OK();
   }
 
   template <typename ArrayType>
   bool ValueOffsetsEqual(const ArrayType& left) {
-    const auto& right = static_cast<const ArrayType&>(right_);
+    const auto& right = checked_cast<const ArrayType&>(right_);
 
     if (left.offset() == 0 && right.offset() == 0) {
       return left.value_offsets()->Equals(*right.value_offsets(),
@@ -399,7 +400,7 @@ class ArrayEqualsVisitor : public RangeEqualsVisitor {
   }
 
   bool CompareBinary(const BinaryArray& left) {
-    const auto& right = static_cast<const BinaryArray&>(right_);
+    const auto& right = checked_cast<const BinaryArray&>(right_);
 
     bool equal_offsets = ValueOffsetsEqual<BinaryArray>(left);
     if (!equal_offsets) {
@@ -451,7 +452,7 @@ class ArrayEqualsVisitor : public RangeEqualsVisitor {
   }
 
   Status Visit(const ListArray& left) {
-    const auto& right = static_cast<const ListArray&>(right_);
+    const auto& right = checked_cast<const ListArray&>(right_);
     bool equal_offsets = ValueOffsetsEqual<ListArray>(left);
     if (!equal_offsets) {
       result_ = false;
@@ -465,7 +466,7 @@ class ArrayEqualsVisitor : public RangeEqualsVisitor {
   }
 
   Status Visit(const DictionaryArray& left) {
-    const auto& right = static_cast<const DictionaryArray&>(right_);
+    const auto& right = checked_cast<const DictionaryArray&>(right_);
     if (!left.dictionary()->Equals(right.dictionary())) {
       result_ = false;
     } else {
@@ -516,13 +517,13 @@ class ApproxEqualsVisitor : public ArrayEqualsVisitor {
 
   Status Visit(const FloatArray& left) {
     result_ =
-        FloatingApproxEquals<FloatType>(left, static_cast<const FloatArray&>(right_));
+        FloatingApproxEquals<FloatType>(left, checked_cast<const FloatArray&>(right_));
     return Status::OK();
   }
 
   Status Visit(const DoubleArray& left) {
     result_ =
-        FloatingApproxEquals<DoubleType>(left, static_cast<const DoubleArray&>(right_));
+        FloatingApproxEquals<DoubleType>(left, checked_cast<const DoubleArray&>(right_));
     return Status::OK();
   }
 };
@@ -596,25 +597,25 @@ class TypeEqualsVisitor {
                               std::is_base_of<DateType, T>::value,
                           Status>::type
   Visit(const T& left) {
-    const auto& right = static_cast<const T&>(right_);
+    const auto& right = checked_cast<const T&>(right_);
     result_ = left.unit() == right.unit();
     return Status::OK();
   }
 
   Status Visit(const TimestampType& left) {
-    const auto& right = static_cast<const TimestampType&>(right_);
+    const auto& right = checked_cast<const TimestampType&>(right_);
     result_ = left.unit() == right.unit() && left.timezone() == right.timezone();
     return Status::OK();
   }
 
   Status Visit(const FixedSizeBinaryType& left) {
-    const auto& right = static_cast<const FixedSizeBinaryType&>(right_);
+    const auto& right = checked_cast<const FixedSizeBinaryType&>(right_);
     result_ = left.byte_width() == right.byte_width();
     return Status::OK();
   }
 
   Status Visit(const Decimal128Type& left) {
-    const auto& right = static_cast<const Decimal128Type&>(right_);
+    const auto& right = checked_cast<const Decimal128Type&>(right_);
     result_ = left.precision() == right.precision() && left.scale() == right.scale();
     return Status::OK();
   }
@@ -624,7 +625,7 @@ class TypeEqualsVisitor {
   Status Visit(const StructType& left) { return VisitChildren(left); }
 
   Status Visit(const UnionType& left) {
-    const auto& right = static_cast<const UnionType&>(right_);
+    const auto& right = checked_cast<const UnionType&>(right_);
 
     if (left.mode() != right.mode() ||
         left.type_codes().size() != right.type_codes().size()) {
@@ -654,7 +655,7 @@ class TypeEqualsVisitor {
   }
 
   Status Visit(const DictionaryType& left) {
-    const auto& right = static_cast<const DictionaryType&>(right_);
+    const auto& right = checked_cast<const DictionaryType&>(right_);
     result_ = left.index_type()->Equals(right.index_type()) &&
               left.dictionary()->Equals(right.dictionary()) &&
               (left.ordered() == right.ordered());
@@ -737,7 +738,7 @@ bool TensorEquals(const Tensor& left, const Tensor& right) {
       if (shape != right.shape()) {
         are_equal = false;
       } else {
-        const auto& type = static_cast<const FixedWidthType&>(*left.type());
+        const auto& type = checked_cast<const FixedWidthType&>(*left.type());
         are_equal =
             StridedTensorContentEquals(0, 0, 0, type.bit_width() / 8, left, right);
       }
diff --git a/cpp/src/arrow/compute/kernels/cast.cc b/cpp/src/arrow/compute/kernels/cast.cc
index 185a966..e5b9eaf 100644
--- a/cpp/src/arrow/compute/kernels/cast.cc
+++ b/cpp/src/arrow/compute/kernels/cast.cc
@@ -35,6 +35,7 @@
 #include "arrow/type.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/macros.h"
 
@@ -348,8 +349,8 @@ struct CastFunctor<TimestampType, TimestampType> {
   void operator()(FunctionContext* ctx, const CastOptions& options,
                   const ArrayData& input, ArrayData* output) {
     // If units are the same, zero copy, otherwise convert
-    const auto& in_type = static_cast<const TimestampType&>(*input.type);
-    const auto& out_type = static_cast<const TimestampType&>(*output->type);
+    const auto& in_type = checked_cast<const TimestampType&>(*input.type);
+    const auto& out_type = checked_cast<const TimestampType&>(*output->type);
 
     if (in_type.unit() == out_type.unit()) {
       CopyData(input, output);
@@ -369,7 +370,7 @@ template <>
 struct CastFunctor<Date32Type, TimestampType> {
   void operator()(FunctionContext* ctx, const CastOptions& options,
                   const ArrayData& input, ArrayData* output) {
-    const auto& in_type = static_cast<const TimestampType&>(*input.type);
+    const auto& in_type = checked_cast<const TimestampType&>(*input.type);
 
     static const int64_t kTimestampToDateFactors[4] = {
         86400LL,                             // SECOND
@@ -387,7 +388,7 @@ template <>
 struct CastFunctor<Date64Type, TimestampType> {
   void operator()(FunctionContext* ctx, const CastOptions& options,
                   const ArrayData& input, ArrayData* output) {
-    const auto& in_type = static_cast<const TimestampType&>(*input.type);
+    const auto& in_type = checked_cast<const TimestampType&>(*input.type);
 
     std::pair<bool, int64_t> conversion =
         kTimeConversionTable[static_cast<int>(in_type.unit())]
@@ -441,8 +442,8 @@ struct CastFunctor<O, I,
     using out_t = typename O::c_type;
 
     // If units are the same, zero copy, otherwise convert
-    const auto& in_type = static_cast<const I&>(*input.type);
-    const auto& out_type = static_cast<const O&>(*output->type);
+    const auto& in_type = checked_cast<const I&>(*input.type);
+    const auto& out_type = checked_cast<const O&>(*output->type);
 
     if (in_type.unit() == out_type.unit()) {
       CopyData(input, output);
@@ -531,7 +532,7 @@ void UnpackFixedSizeBinaryDictionary(FunctionContext* ctx, const Array& indices,
 
   const index_c_type* in = GetValues<index_c_type>(*indices.data(), 1);
   int32_t byte_width =
-      static_cast<const FixedSizeBinaryType&>(*output->type).byte_width();
+      checked_cast<const FixedSizeBinaryType&>(*output->type).byte_width();
 
   uint8_t* out = output->buffers[1]->mutable_data() + byte_width * output->offset;
 
@@ -562,10 +563,10 @@ struct CastFunctor<
                   const ArrayData& input, ArrayData* output) {
     DictionaryArray dict_array(input.Copy());
 
-    const DictionaryType& type = static_cast<const DictionaryType&>(*input.type);
+    const DictionaryType& type = checked_cast<const DictionaryType&>(*input.type);
     const DataType& values_type = *type.dictionary()->type();
     const FixedSizeBinaryArray& dictionary =
-        static_cast<const FixedSizeBinaryArray&>(*type.dictionary());
+        checked_cast<const FixedSizeBinaryArray&>(*type.dictionary());
 
     // Check if values and output type match
     DCHECK(values_type.Equals(*output->type))
@@ -600,7 +601,7 @@ Status UnpackBinaryDictionary(FunctionContext* ctx, const Array& indices,
   using index_c_type = typename IndexType::c_type;
   std::unique_ptr<ArrayBuilder> builder;
   RETURN_NOT_OK(MakeBuilder(ctx->memory_pool(), output->type, &builder));
-  BinaryBuilder* binary_builder = static_cast<BinaryBuilder*>(builder.get());
+  BinaryBuilder* binary_builder = checked_cast<BinaryBuilder*>(builder.get());
 
   const index_c_type* in = GetValues<index_c_type>(*indices.data(), 1);
   if (indices.null_count() != 0) {
@@ -642,9 +643,9 @@ struct CastFunctor<T, DictionaryType,
                   const ArrayData& input, ArrayData* output) {
     DictionaryArray dict_array(input.Copy());
 
-    const DictionaryType& type = static_cast<const DictionaryType&>(*input.type);
+    const DictionaryType& type = checked_cast<const DictionaryType&>(*input.type);
     const DataType& values_type = *type.dictionary()->type();
-    const BinaryArray& dictionary = static_cast<const BinaryArray&>(*type.dictionary());
+    const BinaryArray& dictionary = checked_cast<const BinaryArray&>(*type.dictionary());
 
     // Check if values and output type match
     DCHECK(values_type.Equals(*output->type))
@@ -702,7 +703,7 @@ struct CastFunctor<T, DictionaryType,
 
     DictionaryArray dict_array(input.Copy());
 
-    const DictionaryType& type = static_cast<const DictionaryType&>(*input.type);
+    const DictionaryType& type = checked_cast<const DictionaryType&>(*input.type);
     const DataType& values_type = *type.dictionary()->type();
 
     // Check if values and output type match
@@ -780,7 +781,7 @@ static Status AllocateIfNotPreallocated(FunctionContext* ctx, const ArrayData& i
     }
 
     if (type_id != Type::NA) {
-      const auto& fw_type = static_cast<const FixedWidthType&>(*out->type);
+      const auto& fw_type = checked_cast<const FixedWidthType&>(*out->type);
 
       int bit_width = fw_type.bit_width();
       int64_t buffer_size = 0;
@@ -985,9 +986,9 @@ Status GetListCastFunc(const DataType& in_type, const std::shared_ptr<DataType>&
     // Kernel will be null
     return Status::OK();
   }
-  const DataType& in_value_type = *static_cast<const ListType&>(in_type).value_type();
+  const DataType& in_value_type = *checked_cast<const ListType&>(in_type).value_type();
   std::shared_ptr<DataType> out_value_type =
-      static_cast<const ListType&>(*out_type).value_type();
+      checked_cast<const ListType&>(*out_type).value_type();
   std::unique_ptr<UnaryKernel> child_caster;
   RETURN_NOT_OK(GetCastFunction(in_value_type, out_value_type, options, &child_caster));
   *kernel =
diff --git a/cpp/src/arrow/compute/kernels/hash.cc b/cpp/src/arrow/compute/kernels/hash.cc
index dbce6e5..1c849d0 100644
--- a/cpp/src/arrow/compute/kernels/hash.cc
+++ b/cpp/src/arrow/compute/kernels/hash.cc
@@ -30,6 +30,7 @@
 #include "arrow/compute/context.h"
 #include "arrow/compute/kernel.h"
 #include "arrow/compute/kernels/util-internal.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/hash-util.h"
 #include "arrow/util/hash.h"
 
@@ -152,7 +153,7 @@ class HashTableKernel<Type, Action, enable_if_null<Type>> : public HashTable {
     if (!initialized_) {
       RETURN_NOT_OK(Init());
     }
-    auto action = static_cast<Action*>(this);
+    auto action = checked_cast<Action*>(this);
     RETURN_NOT_OK(action->Reserve(arr.length));
     for (int64_t i = 0; i < arr.length; ++i) {
       action->ObserveNull();
@@ -242,7 +243,7 @@ class HashTableKernel<
     }
 
     const T* values = GetValues<T>(arr, 1);
-    auto action = static_cast<Action*>(this);
+    auto action = checked_cast<Action*>(this);
 
     RETURN_NOT_OK(action->Reserve(arr.length));
 
@@ -326,7 +327,7 @@ class HashTableKernel<Type, Action, enable_if_boolean<Type>> : public HashTable
   }
 
   Status Append(const ArrayData& arr) override {
-    auto action = static_cast<Action*>(this);
+    auto action = checked_cast<Action*>(this);
 
     RETURN_NOT_OK(action->Reserve(arr.length));
 
@@ -423,7 +424,7 @@ class HashTableKernel<Type, Action, enable_if_binary<Type>> : public HashTable {
       data = GetValues<uint8_t>(arr, 2);
     }
 
-    auto action = static_cast<Action*>(this);
+    auto action = checked_cast<Action*>(this);
     RETURN_NOT_OK(action->Reserve(arr.length));
 
 #define HASH_INNER_LOOP()                                                           \
@@ -521,7 +522,7 @@ class HashTableKernel<Type, Action, enable_if_fixed_size_binary<Type>>
  public:
   HashTableKernel(const std::shared_ptr<DataType>& type, MemoryPool* pool)
       : HashTable(type, pool), dict_data_(pool), dict_size_(0) {
-    const auto& fw_type = static_cast<const FixedSizeBinaryType&>(*type);
+    const auto& fw_type = checked_cast<const FixedSizeBinaryType&>(*type);
     byte_width_ = fw_type.bit_width() / 8;
   }
 
@@ -537,7 +538,7 @@ class HashTableKernel<Type, Action, enable_if_fixed_size_binary<Type>>
 
     const uint8_t* data = GetValues<uint8_t>(arr, 1);
 
-    auto action = static_cast<Action*>(this);
+    auto action = checked_cast<Action*>(this);
     RETURN_NOT_OK(action->Reserve(arr.length));
 
 #define HASH_INNER_LOOP()                                                      \
@@ -644,7 +645,7 @@ class HashTableKernel<Type, Action, enable_if_8bit_int<Type>> : public HashTable
 
   Status Append(const ArrayData& arr) override {
     const T* values = GetValues<T>(arr, 1);
-    auto action = static_cast<Action*>(this);
+    auto action = checked_cast<Action*>(this);
     RETURN_NOT_OK(action->Reserve(arr.length));
 
 #define HASH_INNER_LOOP()                                      \
diff --git a/cpp/src/arrow/io/io-memory-test.cc b/cpp/src/arrow/io/io-memory-test.cc
index 8c2e8c3..0652fe7 100644
--- a/cpp/src/arrow/io/io-memory-test.cc
+++ b/cpp/src/arrow/io/io-memory-test.cc
@@ -29,6 +29,7 @@
 #include "arrow/memory_pool.h"
 #include "arrow/status.h"
 #include "arrow/test-util.h"
+#include "arrow/util/checked_cast.h"
 
 namespace arrow {
 namespace io {
@@ -73,7 +74,7 @@ TEST_F(TestBufferOutputStream, WriteAfterFinish) {
   std::string data = "data123456";
   ASSERT_OK(stream_->Write(data));
 
-  auto buffer_stream = static_cast<BufferOutputStream*>(stream_.get());
+  auto buffer_stream = checked_cast<BufferOutputStream*>(stream_.get());
 
   std::shared_ptr<Buffer> buffer;
   ASSERT_OK(buffer_stream->Finish(&buffer));
diff --git a/cpp/src/arrow/ipc/feather-test.cc b/cpp/src/arrow/ipc/feather-test.cc
index ae1489a..cea5176 100644
--- a/cpp/src/arrow/ipc/feather-test.cc
+++ b/cpp/src/arrow/ipc/feather-test.cc
@@ -31,6 +31,7 @@
 #include "arrow/pretty_print.h"
 #include "arrow/table.h"
 #include "arrow/test-util.h"
+#include "arrow/util/checked_cast.h"
 
 namespace arrow {
 namespace ipc {
@@ -365,7 +366,7 @@ TEST_F(TestTableWriter, TimeTypes) {
   std::shared_ptr<Array> date_array;
   ArrayFromVector<Date32Type, int32_t>(is_valid, date_values_vec, &date_array);
 
-  const auto& prim_values = static_cast<const PrimitiveArray&>(*values);
+  const auto& prim_values = checked_cast<const PrimitiveArray&>(*values);
   BufferVector buffers = {prim_values.null_bitmap(), prim_values.values()};
 
   std::vector<std::shared_ptr<ArrayData>> arrays;
diff --git a/cpp/src/arrow/ipc/feather.cc b/cpp/src/arrow/ipc/feather.cc
index 7e762ac..1cd3007 100644
--- a/cpp/src/arrow/ipc/feather.cc
+++ b/cpp/src/arrow/ipc/feather.cc
@@ -38,6 +38,7 @@
 #include "arrow/table.h"
 #include "arrow/type.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 #include "arrow/visitor.h"
 
@@ -611,7 +612,7 @@ class TableWriter::TableWriterImpl : public ArrayVisitor {
     const uint8_t* values_buffer = nullptr;
 
     if (is_binary_like(values.type_id())) {
-      const auto& bin_values = static_cast<const BinaryArray&>(values);
+      const auto& bin_values = checked_cast<const BinaryArray&>(values);
 
       int64_t offset_bytes = sizeof(int32_t) * (values.length() + 1);
 
@@ -632,8 +633,8 @@ class TableWriter::TableWriterImpl : public ArrayVisitor {
         values_buffer = bin_values.value_data()->data();
       }
     } else {
-      const auto& prim_values = static_cast<const PrimitiveArray&>(values);
-      const auto& fw_type = static_cast<const FixedWidthType&>(*values.type());
+      const auto& prim_values = checked_cast<const PrimitiveArray&>(values);
+      const auto& fw_type = checked_cast<const FixedWidthType&>(*values.type());
 
       values_bytes = BitUtil::BytesForBits(values.length() * fw_type.bit_width());
 
@@ -688,7 +689,7 @@ class TableWriter::TableWriterImpl : public ArrayVisitor {
 #undef VISIT_PRIMITIVE
 
   Status Visit(const DictionaryArray& values) override {
-    const auto& dict_type = static_cast<const DictionaryType&>(*values.type());
+    const auto& dict_type = checked_cast<const DictionaryType&>(*values.type());
 
     if (!is_integer(values.indices()->type_id())) {
       return Status::Invalid("Category values must be integers");
@@ -707,7 +708,7 @@ class TableWriter::TableWriterImpl : public ArrayVisitor {
 
   Status Visit(const TimestampArray& values) override {
     RETURN_NOT_OK(WritePrimitiveValues(values));
-    const auto& ts_type = static_cast<const TimestampType&>(*values.type());
+    const auto& ts_type = checked_cast<const TimestampType&>(*values.type());
     current_column_->SetTimestamp(ts_type.unit(), ts_type.timezone());
     return Status::OK();
   }
@@ -720,7 +721,7 @@ class TableWriter::TableWriterImpl : public ArrayVisitor {
 
   Status Visit(const Time32Array& values) override {
     RETURN_NOT_OK(WritePrimitiveValues(values));
-    auto unit = static_cast<const Time32Type&>(*values.type()).unit();
+    auto unit = checked_cast<const Time32Type&>(*values.type()).unit();
     current_column_->SetTime(unit);
     return Status::OK();
   }
diff --git a/cpp/src/arrow/ipc/ipc-read-write-test.cc b/cpp/src/arrow/ipc/ipc-read-write-test.cc
index 437585f..bbc279f 100644
--- a/cpp/src/arrow/ipc/ipc-read-write-test.cc
+++ b/cpp/src/arrow/ipc/ipc-read-write-test.cc
@@ -38,6 +38,7 @@
 #include "arrow/tensor.h"
 #include "arrow/test-util.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 
 namespace arrow {
 namespace ipc {
@@ -653,14 +654,14 @@ void CheckBatchDictionaries(const RecordBatch& batch) {
   // Check that dictionaries that should be the same are the same
   auto schema = batch.schema();
 
-  const auto& t0 = static_cast<const DictionaryType&>(*schema->field(0)->type());
-  const auto& t1 = static_cast<const DictionaryType&>(*schema->field(1)->type());
+  const auto& t0 = checked_cast<const DictionaryType&>(*schema->field(0)->type());
+  const auto& t1 = checked_cast<const DictionaryType&>(*schema->field(1)->type());
 
   ASSERT_EQ(t0.dictionary().get(), t1.dictionary().get());
 
   // Same dictionary used for list values
-  const auto& t3 = static_cast<const ListType&>(*schema->field(3)->type());
-  const auto& t3_value = static_cast<const DictionaryType&>(*t3.value_type());
+  const auto& t3 = checked_cast<const ListType&>(*schema->field(3)->type());
+  const auto& t3_value = checked_cast<const DictionaryType&>(*t3.value_type());
   ASSERT_EQ(t0.dictionary().get(), t3_value.dictionary().get());
 }
 
diff --git a/cpp/src/arrow/ipc/json-internal.cc b/cpp/src/arrow/ipc/json-internal.cc
index 204bbc4..cb9cf51 100644
--- a/cpp/src/arrow/ipc/json-internal.cc
+++ b/cpp/src/arrow/ipc/json-internal.cc
@@ -33,6 +33,7 @@
 #include "arrow/type.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/string.h"
@@ -157,7 +158,7 @@ class SchemaWriter {
     writer_->EndObject();
 
     if (type.id() == Type::DICTIONARY) {
-      const auto& dict_type = static_cast<const DictionaryType&>(type);
+      const auto& dict_type = checked_cast<const DictionaryType&>(type);
       RETURN_NOT_OK(WriteDictionaryMetadata(dict_type));
 
       const DataType& dictionary_type = *dict_type.dictionary()->type();
@@ -516,13 +517,13 @@ class ArrayWriter {
   Status Visit(const ListArray& array) {
     WriteValidityField(array);
     WriteIntegerField("OFFSET", array.raw_value_offsets(), array.length() + 1);
-    const auto& type = static_cast<const ListType&>(*array.type());
+    const auto& type = checked_cast<const ListType&>(*array.type());
     return WriteChildren(type.children(), {array.values()});
   }
 
   Status Visit(const StructArray& array) {
     WriteValidityField(array);
-    const auto& type = static_cast<const StructType&>(*array.type());
+    const auto& type = checked_cast<const StructType&>(*array.type());
     std::vector<std::shared_ptr<Array>> children;
     children.reserve(array.num_fields());
     for (int i = 0; i < array.num_fields(); ++i) {
@@ -533,7 +534,7 @@ class ArrayWriter {
 
   Status Visit(const UnionArray& array) {
     WriteValidityField(array);
-    const auto& type = static_cast<const UnionType&>(*array.type());
+    const auto& type = checked_cast<const UnionType&>(*array.type());
 
     WriteIntegerField("TYPE_ID", array.raw_type_ids(), array.length());
     if (type.mode() == UnionMode::DENSE) {
@@ -690,7 +691,7 @@ static Status GetTime(const RjObject& json_type, std::shared_ptr<DataType>* type
     return Status::Invalid(ss.str());
   }
 
-  const auto& fw_type = static_cast<const FixedWidthType&>(**type);
+  const auto& fw_type = checked_cast<const FixedWidthType&>(**type);
 
   int bit_width = it_bit_width->value.GetInt();
   if (bit_width != fw_type.bit_width()) {
diff --git a/cpp/src/arrow/ipc/metadata-internal.cc b/cpp/src/arrow/ipc/metadata-internal.cc
index d543239..aaa3cc5 100644
--- a/cpp/src/arrow/ipc/metadata-internal.cc
+++ b/cpp/src/arrow/ipc/metadata-internal.cc
@@ -39,6 +39,7 @@
 #include "arrow/tensor.h"
 #include "arrow/type.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/key_value_metadata.h"
 #include "arrow/util/logging.h"
 
@@ -189,7 +190,7 @@ static Status UnionToFlatBuffer(FBB& fbb, const DataType& type,
                                 DictionaryMemo* dictionary_memo, Offset* offset) {
   RETURN_NOT_OK(AppendChildFields(fbb, type, out_children, dictionary_memo));
 
-  const auto& union_type = static_cast<const UnionType&>(type);
+  const auto& union_type = checked_cast<const UnionType&>(type);
 
   flatbuf::UnionMode mode = union_type.mode() == UnionMode::SPARSE
                                 ? flatbuf::UnionMode_Sparse
@@ -348,7 +349,7 @@ static Status TypeToFlatbuffer(FBB& fbb, const DataType& type,
     // In this library, the dictionary "type" is a logical construct. Here we
     // pass through to the value type, as we've already captured the index
     // type in the DictionaryEncoding metadata in the parent field
-    value_type = static_cast<const DictionaryType&>(type).dictionary()->type().get();
+    value_type = checked_cast<const DictionaryType&>(type).dictionary()->type().get();
   }
 
   switch (value_type->id()) {
@@ -389,7 +390,7 @@ static Status TypeToFlatbuffer(FBB& fbb, const DataType& type,
       *offset = FloatToFlatbuffer(fbb, flatbuf::Precision_DOUBLE);
       break;
     case Type::FIXED_SIZE_BINARY: {
-      const auto& fw_type = static_cast<const FixedSizeBinaryType&>(*value_type);
+      const auto& fw_type = checked_cast<const FixedSizeBinaryType&>(*value_type);
       *out_type = flatbuf::Type_FixedSizeBinary;
       *offset = flatbuf::CreateFixedSizeBinary(fbb, fw_type.byte_width()).Union();
     } break;
@@ -410,17 +411,17 @@ static Status TypeToFlatbuffer(FBB& fbb, const DataType& type,
       *offset = flatbuf::CreateDate(fbb, flatbuf::DateUnit_MILLISECOND).Union();
       break;
     case Type::TIME32: {
-      const auto& time_type = static_cast<const Time32Type&>(*value_type);
+      const auto& time_type = checked_cast<const Time32Type&>(*value_type);
       *out_type = flatbuf::Type_Time;
       *offset = flatbuf::CreateTime(fbb, ToFlatbufferUnit(time_type.unit()), 32).Union();
     } break;
     case Type::TIME64: {
-      const auto& time_type = static_cast<const Time64Type&>(*value_type);
+      const auto& time_type = checked_cast<const Time64Type&>(*value_type);
       *out_type = flatbuf::Type_Time;
       *offset = flatbuf::CreateTime(fbb, ToFlatbufferUnit(time_type.unit()), 64).Union();
     } break;
     case Type::TIMESTAMP: {
-      const auto& ts_type = static_cast<const TimestampType&>(*value_type);
+      const auto& ts_type = checked_cast<const TimestampType&>(*value_type);
       *out_type = flatbuf::Type_Timestamp;
 
       flatbuf::TimeUnit fb_unit = ToFlatbufferUnit(ts_type.unit());
@@ -431,7 +432,7 @@ static Status TypeToFlatbuffer(FBB& fbb, const DataType& type,
       *offset = flatbuf::CreateTimestamp(fbb, fb_unit, fb_timezone).Union();
     } break;
     case Type::DECIMAL: {
-      const auto& dec_type = static_cast<const Decimal128Type&>(*value_type);
+      const auto& dec_type = checked_cast<const Decimal128Type&>(*value_type);
       *out_type = flatbuf::Type_Decimal;
       *offset =
           flatbuf::CreateDecimal(fbb, dec_type.precision(), dec_type.scale()).Union();
@@ -501,7 +502,7 @@ static DictionaryOffset GetDictionaryEncoding(FBB& fbb, const DictionaryType& ty
   // We assume that the dictionary index type (as an integer) has already been
   // validated elsewhere, and can safely assume we are dealing with signed
   // integers
-  const auto& fw_index_type = static_cast<const FixedWidthType&>(*type.index_type());
+  const auto& fw_index_type = checked_cast<const FixedWidthType&>(*type.index_type());
 
   auto index_type_offset = flatbuf::CreateInt(fbb, fw_index_type.bit_width(), true);
 
@@ -525,7 +526,7 @@ static Status FieldToFlatbuffer(FBB& fbb, const Field& field,
   DictionaryOffset dictionary = 0;
   if (field.type()->id() == Type::DICTIONARY) {
     dictionary = GetDictionaryEncoding(
-        fbb, static_cast<const DictionaryType&>(*field.type()), dictionary_memo);
+        fbb, checked_cast<const DictionaryType&>(*field.type()), dictionary_memo);
   }
 
   // TODO: produce the list of VectorTypes
diff --git a/cpp/src/arrow/ipc/writer.cc b/cpp/src/arrow/ipc/writer.cc
index cdb3399..84830e6 100644
--- a/cpp/src/arrow/ipc/writer.cc
+++ b/cpp/src/arrow/ipc/writer.cc
@@ -38,6 +38,7 @@
 #include "arrow/tensor.h"
 #include "arrow/type.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 
 namespace arrow {
@@ -236,7 +237,7 @@ class RecordBatchSerializer : public ArrayVisitor {
   Status VisitFixedWidth(const ArrayType& array) {
     std::shared_ptr<Buffer> data = array.values();
 
-    const auto& fw_type = static_cast<const FixedWidthType&>(*array.type());
+    const auto& fw_type = checked_cast<const FixedWidthType&>(*array.type());
     const int64_t type_width = fw_type.bit_width() / 8;
     int64_t min_length = PaddedLength(array.length() * type_width);
 
@@ -393,7 +394,7 @@ class RecordBatchSerializer : public ArrayVisitor {
 
     --max_recursion_depth_;
     if (array.mode() == UnionMode::DENSE) {
-      const auto& type = static_cast<const UnionType&>(*array.type());
+      const auto& type = checked_cast<const UnionType&>(*array.type());
 
       std::shared_ptr<Buffer> value_offsets;
       RETURN_NOT_OK(GetTruncatedBuffer<int32_t>(offset, length, array.value_offsets(),
@@ -595,7 +596,7 @@ Status WriteStridedTensorData(int dim_index, int64_t offset, int elem_size,
 
 Status GetContiguousTensor(const Tensor& tensor, MemoryPool* pool,
                            std::unique_ptr<Tensor>* out) {
-  const auto& type = static_cast<const FixedWidthType&>(*tensor.type());
+  const auto& type = checked_cast<const FixedWidthType&>(*tensor.type());
   const int elem_size = type.bit_width() / 8;
 
   // TODO(wesm): Do we care enough about this temporary allocation to pass in
@@ -639,7 +640,7 @@ Status WriteTensor(const Tensor& tensor, io::OutputStream* dst, int32_t* metadat
     }
   } else {
     Tensor dummy(tensor.type(), tensor.data(), tensor.shape());
-    const auto& type = static_cast<const FixedWidthType&>(*tensor.type());
+    const auto& type = checked_cast<const FixedWidthType&>(*tensor.type());
     RETURN_NOT_OK(WriteTensorHeader(dummy, dst, metadata_length, body_length));
     // It's important to align the stream position again so that the tensor data
     // is aligned.
diff --git a/cpp/src/arrow/pretty_print.cc b/cpp/src/arrow/pretty_print.cc
index 38ccb7b..6663007 100644
--- a/cpp/src/arrow/pretty_print.cc
+++ b/cpp/src/arrow/pretty_print.cc
@@ -27,6 +27,7 @@
 #include "arrow/status.h"
 #include "arrow/type.h"
 #include "arrow/type_traits.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/string.h"
 #include "arrow/visitor_inline.h"
@@ -379,7 +380,7 @@ Status SchemaPrinter::PrintType(const DataType& type) {
 
     indent_ += 2;
     WriteIndented("dictionary: ");
-    const auto& dict_type = static_cast<const DictionaryType&>(type);
+    const auto& dict_type = checked_cast<const DictionaryType&>(type);
     RETURN_NOT_OK(PrettyPrint(*dict_type.dictionary(), indent_, sink_));
     indent_ -= 2;
   } else {
diff --git a/cpp/src/arrow/python/arrow_to_pandas.cc b/cpp/src/arrow/python/arrow_to_pandas.cc
index 41a07d0..f8887d4 100644
--- a/cpp/src/arrow/python/arrow_to_pandas.cc
+++ b/cpp/src/arrow/python/arrow_to_pandas.cc
@@ -36,6 +36,7 @@
 #include "arrow/type_fwd.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/macros.h"
@@ -109,7 +110,7 @@ static inline bool ListTypeSupported(const DataType& type) {
       // The above types are all supported.
       return true;
     case Type::LIST: {
-      const ListType& list_type = static_cast<const ListType&>(type);
+      const ListType& list_type = checked_cast<const ListType&>(type);
       return ListTypeSupported(*list_type.value_type());
     }
     default:
@@ -139,9 +140,9 @@ inline void set_numpy_metadata(int type, DataType* datatype, PyArray_Descr* out)
   if (type == NPY_DATETIME) {
     auto date_dtype = reinterpret_cast<PyArray_DatetimeDTypeMetaData*>(out->c_metadata);
     if (datatype->id() == Type::TIMESTAMP) {
-      auto timestamp_type = static_cast<TimestampType*>(datatype);
+      const auto& timestamp_type = checked_cast<const TimestampType&>(*datatype);
 
-      switch (timestamp_type->unit()) {
+      switch (timestamp_type.unit()) {
         case TimestampType::Unit::SECOND:
           date_dtype->meta.base = NPY_FR_s;
           break;
@@ -286,7 +287,7 @@ inline const T* GetPrimitiveValues(const Array& arr) {
   if (arr.length() == 0) {
     return nullptr;
   }
-  const auto& prim_arr = static_cast<const PrimitiveArray&>(arr);
+  const auto& prim_arr = checked_cast<const PrimitiveArray&>(arr);
   const T* raw_values = reinterpret_cast<const T*>(prim_arr.values()->data());
   return raw_values + arr.offset();
 }
@@ -334,7 +335,7 @@ static Status ConvertBooleanWithNulls(PandasOptions options, const ChunkedArray&
                                       PyObject** out_values) {
   PyAcquireGIL lock;
   for (int c = 0; c < data.num_chunks(); c++) {
-    const auto& arr = static_cast<const BooleanArray&>(*data.chunk(c));
+    const auto& arr = checked_cast<const BooleanArray&>(*data.chunk(c));
 
     for (int64_t i = 0; i < arr.length(); ++i) {
       if (arr.IsNull(i)) {
@@ -357,7 +358,7 @@ static Status ConvertBooleanWithNulls(PandasOptions options, const ChunkedArray&
 static void ConvertBooleanNoNulls(PandasOptions options, const ChunkedArray& data,
                                   uint8_t* out_values) {
   for (int c = 0; c < data.num_chunks(); c++) {
-    const auto& arr = static_cast<const BooleanArray&>(*data.chunk(c));
+    const auto& arr = checked_cast<const BooleanArray&>(*data.chunk(c));
     for (int64_t i = 0; i < arr.length(); ++i) {
       *out_values++ = static_cast<uint8_t>(arr.Value(i));
     }
@@ -368,18 +369,18 @@ template <typename T>
 static Status ConvertIntegerObjects(PandasOptions options, const ChunkedArray& data,
                                     PyObject** out_values) {
   PyAcquireGIL lock;
+  constexpr bool is_signed = std::is_signed<T>::value;
   for (int c = 0; c < data.num_chunks(); c++) {
     const auto& arr = *data.chunk(c);
-    const T* in_values = GetPrimitiveValues<T>(arr);
+    const auto* in_values = GetPrimitiveValues<T>(arr);
 
     for (int i = 0; i < arr.length(); ++i) {
       if (arr.IsNull(i)) {
         Py_INCREF(Py_None);
         *out_values++ = Py_None;
       } else {
-        *out_values++ = std::is_signed<T>::value
-                            ? PyLong_FromLongLong(in_values[i])
-                            : PyLong_FromUnsignedLongLong(in_values[i]);
+        *out_values++ = is_signed ? PyLong_FromLongLong(in_values[i])
+                                  : PyLong_FromUnsignedLongLong(in_values[i]);
         RETURN_IF_PYERROR();
       }
     }
@@ -393,7 +394,7 @@ inline Status ConvertBinaryLike(PandasOptions options, const ChunkedArray& data,
   using ArrayType = typename TypeTraits<Type>::ArrayType;
   PyAcquireGIL lock;
   for (int c = 0; c < data.num_chunks(); c++) {
-    const auto& arr = static_cast<const ArrayType&>(*data.chunk(c));
+    const auto& arr = checked_cast<const ArrayType&>(*data.chunk(c));
 
     const uint8_t* data_ptr;
     int32_t length;
@@ -439,7 +440,7 @@ inline Status ConvertFixedSizeBinary(PandasOptions options, const ChunkedArray&
                                      PyObject** out_values) {
   PyAcquireGIL lock;
   for (int c = 0; c < data.num_chunks(); c++) {
-    auto arr = static_cast<FixedSizeBinaryArray*>(data.chunk(c).get());
+    auto arr = checked_cast<FixedSizeBinaryArray*>(data.chunk(c).get());
 
     const uint8_t* data_ptr;
     int32_t length =
@@ -473,14 +474,14 @@ inline Status ConvertStruct(PandasOptions options, const ChunkedArray& data,
     return Status::OK();
   }
   // ChunkedArray has at least one chunk
-  auto arr = static_cast<const StructArray*>(data.chunk(0).get());
+  auto arr = checked_cast<const StructArray*>(data.chunk(0).get());
   // Use it to cache the struct type and number of fields for all chunks
   int32_t num_fields = arr->num_fields();
   auto array_type = arr->type();
   std::vector<OwnedRef> fields_data(num_fields);
   OwnedRef dict_item;
   for (int c = 0; c < data.num_chunks(); c++) {
-    auto arr = static_cast<const StructArray*>(data.chunk(c).get());
+    auto arr = checked_cast<const StructArray*>(data.chunk(c).get());
     // Convert the struct arrays first
     for (int32_t i = 0; i < num_fields; i++) {
       PyObject* numpy_array;
@@ -533,12 +534,12 @@ template <typename ArrowType>
 inline Status ConvertListsLike(PandasOptions options, const std::shared_ptr<Column>& col,
                                PyObject** out_values) {
   const ChunkedArray& data = *col->data().get();
-  const auto& list_type = static_cast<const ListType&>(*col->type());
+  const auto& list_type = checked_cast<const ListType&>(*col->type());
 
   // Get column of underlying value arrays
   std::vector<std::shared_ptr<Array>> value_arrays;
   for (int c = 0; c < data.num_chunks(); c++) {
-    const auto& arr = static_cast<const ListArray&>(*data.chunk(c));
+    const auto& arr = checked_cast<const ListArray&>(*data.chunk(c));
     value_arrays.emplace_back(arr.values());
   }
   auto flat_column = std::make_shared<Column>(list_type.value_field(), value_arrays);
@@ -642,7 +643,7 @@ static Status ConvertTimes(PandasOptions options, const ChunkedArray& data,
   PyDateTime_IMPORT;
 
   for (int c = 0; c < data.num_chunks(); c++) {
-    const auto& arr = static_cast<const ArrayType&>(*data.chunk(c));
+    const auto& arr = checked_cast<const ArrayType&>(*data.chunk(c));
     auto type = std::dynamic_pointer_cast<TYPE>(arr.type());
     DCHECK(type);
 
@@ -672,7 +673,7 @@ static Status ConvertDecimals(PandasOptions options, const ChunkedArray& data,
   PyObject* decimal_constructor = Decimal.obj();
 
   for (int c = 0; c < data.num_chunks(); c++) {
-    const auto& arr = static_cast<const arrow::Decimal128Array&>(*data.chunk(c));
+    const auto& arr = checked_cast<const arrow::Decimal128Array&>(*data.chunk(c));
 
     for (int64_t i = 0; i < arr.length(); ++i) {
       if (arr.IsNull(i)) {
@@ -978,15 +979,15 @@ class DatetimeBlock : public PandasBlock {
       // TODO(wesm): Do we want to make sure to zero out the milliseconds?
       ConvertDatetimeNanos<int64_t, 1000000L>(data, out_buffer);
     } else if (type == Type::TIMESTAMP) {
-      auto ts_type = static_cast<TimestampType*>(col->type().get());
+      const auto& ts_type = checked_cast<const TimestampType&>(*col->type());
 
-      if (ts_type->unit() == TimeUnit::NANO) {
+      if (ts_type.unit() == TimeUnit::NANO) {
         ConvertNumericNullable<int64_t>(data, kPandasTimestampNull, out_buffer);
-      } else if (ts_type->unit() == TimeUnit::MICRO) {
+      } else if (ts_type.unit() == TimeUnit::MICRO) {
         ConvertDatetimeNanos<int64_t, 1000L>(data, out_buffer);
-      } else if (ts_type->unit() == TimeUnit::MILLI) {
+      } else if (ts_type.unit() == TimeUnit::MILLI) {
         ConvertDatetimeNanos<int64_t, 1000000L>(data, out_buffer);
-      } else if (ts_type->unit() == TimeUnit::SECOND) {
+      } else if (ts_type.unit() == TimeUnit::SECOND) {
         ConvertDatetimeNanos<int64_t, 1000000000L>(data, out_buffer);
       } else {
         return Status::NotImplemented("Unsupported time unit");
@@ -1053,7 +1054,7 @@ class CategoricalBlock : public PandasBlock {
 
     // Sniff the first chunk
     const std::shared_ptr<Array> arr_first = data.chunk(0);
-    const auto& dict_arr_first = static_cast<const DictionaryArray&>(*arr_first);
+    const auto& dict_arr_first = checked_cast<const DictionaryArray&>(*arr_first);
     const auto indices_first =
         std::static_pointer_cast<ArrayType>(dict_arr_first.indices());
 
@@ -1092,9 +1093,9 @@ class CategoricalBlock : public PandasBlock {
 
       for (int c = 0; c < data.num_chunks(); c++) {
         const std::shared_ptr<Array> arr = data.chunk(c);
-        const auto& dict_arr = static_cast<const DictionaryArray&>(*arr);
+        const auto& dict_arr = checked_cast<const DictionaryArray&>(*arr);
 
-        const auto& indices = static_cast<const ArrayType&>(*dict_arr.indices());
+        const auto& indices = checked_cast<const ArrayType&>(*dict_arr.indices());
         auto in_values = reinterpret_cast<const T*>(indices.raw_values());
 
         RETURN_NOT_OK(CheckIndices(indices, dict_arr.dictionary()->length()));
@@ -1125,7 +1126,7 @@ class CategoricalBlock : public PandasBlock {
       converted_col = col;
     }
 
-    const auto& dict_type = static_cast<const DictionaryType&>(*converted_col->type());
+    const auto& dict_type = checked_cast<const DictionaryType&>(*converted_col->type());
 
     switch (dict_type.index_type()->id()) {
       case Type::INT8:
@@ -1327,7 +1328,7 @@ static Status GetPandasBlockType(const Column& col, const PandasOptions& options
       *output_type = PandasBlock::DATETIME;
       break;
     case Type::TIMESTAMP: {
-      const auto& ts_type = static_cast<const TimestampType&>(*col.type());
+      const auto& ts_type = checked_cast<const TimestampType&>(*col.type());
       if (ts_type.timezone() != "") {
         *output_type = PandasBlock::DATETIME_WITH_TZ;
       } else {
@@ -1393,7 +1394,7 @@ class DataFrameBlockCreator {
         block = std::make_shared<CategoricalBlock>(options_, pool_, table_->num_rows());
         categorical_blocks_[i] = block;
       } else if (output_type == PandasBlock::DATETIME_WITH_TZ) {
-        const auto& ts_type = static_cast<const TimestampType&>(*col->type());
+        const auto& ts_type = checked_cast<const TimestampType&>(*col->type());
         block = std::make_shared<DatetimeTZBlock>(options_, ts_type.timezone(),
                                                   table_->num_rows());
         RETURN_NOT_OK(block->Allocate());
diff --git a/cpp/src/arrow/python/arrow_to_python.cc b/cpp/src/arrow/python/arrow_to_python.cc
index 6ae5d43..57884e5 100644
--- a/cpp/src/arrow/python/arrow_to_python.cc
+++ b/cpp/src/arrow/python/arrow_to_python.cc
@@ -33,6 +33,7 @@
 #include "arrow/io/memory.h"
 #include "arrow/ipc/reader.h"
 #include "arrow/table.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 
 #include "arrow/python/common.h"
@@ -63,7 +64,7 @@ Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
 Status DeserializeDict(PyObject* context, const Array& array, int64_t start_idx,
                        int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
                        PyObject** out) {
-  const auto& data = static_cast<const StructArray&>(array);
+  const auto& data = checked_cast<const StructArray&>(array);
   OwnedRef keys, vals;
   OwnedRef result(PyDict_New());
   RETURN_IF_PYERROR();
@@ -93,7 +94,7 @@ Status DeserializeDict(PyObject* context, const Array& array, int64_t start_idx,
 
 Status DeserializeArray(const Array& array, int64_t offset, PyObject* base,
                         const SerializedPyObject& blobs, PyObject** out) {
-  int32_t index = static_cast<const Int32Array&>(array).Value(offset);
+  int32_t index = checked_cast<const Int32Array&>(array).Value(offset);
   RETURN_NOT_OK(py::TensorToNdarray(blobs.tensors[index], base, out));
   // Mark the array as immutable
   OwnedRef flags(PyObject_GetAttrString(*out, "flags"));
@@ -109,43 +110,43 @@ Status GetValue(PyObject* context, const UnionArray& parent, const Array& arr,
                 const SerializedPyObject& blobs, PyObject** result) {
   switch (arr.type()->id()) {
     case Type::BOOL:
-      *result = PyBool_FromLong(static_cast<const BooleanArray&>(arr).Value(index));
+      *result = PyBool_FromLong(checked_cast<const BooleanArray&>(arr).Value(index));
       return Status::OK();
     case Type::INT64:
-      *result = PyLong_FromSsize_t(static_cast<const Int64Array&>(arr).Value(index));
+      *result = PyLong_FromSsize_t(checked_cast<const Int64Array&>(arr).Value(index));
       return Status::OK();
     case Type::BINARY: {
       int32_t nchars;
-      const uint8_t* str = static_cast<const BinaryArray&>(arr).GetValue(index, &nchars);
+      const uint8_t* str = checked_cast<const BinaryArray&>(arr).GetValue(index, &nchars);
       *result = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(str), nchars);
       return CheckPyError();
     }
     case Type::STRING: {
       int32_t nchars;
-      const uint8_t* str = static_cast<const StringArray&>(arr).GetValue(index, &nchars);
+      const uint8_t* str = checked_cast<const StringArray&>(arr).GetValue(index, &nchars);
       *result = PyUnicode_FromStringAndSize(reinterpret_cast<const char*>(str), nchars);
       return CheckPyError();
     }
     case Type::HALF_FLOAT: {
-      *result = PyHalf_FromHalf(static_cast<const HalfFloatArray&>(arr).Value(index));
+      *result = PyHalf_FromHalf(checked_cast<const HalfFloatArray&>(arr).Value(index));
       RETURN_IF_PYERROR();
       return Status::OK();
     }
     case Type::FLOAT:
-      *result = PyFloat_FromDouble(static_cast<const FloatArray&>(arr).Value(index));
+      *result = PyFloat_FromDouble(checked_cast<const FloatArray&>(arr).Value(index));
       return Status::OK();
     case Type::DOUBLE:
-      *result = PyFloat_FromDouble(static_cast<const DoubleArray&>(arr).Value(index));
+      *result = PyFloat_FromDouble(checked_cast<const DoubleArray&>(arr).Value(index));
       return Status::OK();
     case Type::DATE64: {
-      RETURN_NOT_OK(PyDateTime_from_int(static_cast<const Date64Array&>(arr).Value(index),
-                                        TimeUnit::MICRO, result));
+      RETURN_NOT_OK(PyDateTime_from_int(
+          checked_cast<const Date64Array&>(arr).Value(index), TimeUnit::MICRO, result));
       RETURN_IF_PYERROR();
       return Status::OK();
     }
     case Type::STRUCT: {
-      const auto& s = static_cast<const StructArray&>(arr);
-      const auto& l = static_cast<const ListArray&>(*s.field(0));
+      const auto& s = checked_cast<const StructArray&>(arr);
+      const auto& l = checked_cast<const ListArray&>(*s.field(0));
       if (s.type()->child(0)->name() == "list") {
         return DeserializeList(context, *l.values(), l.value_offset(index),
                                l.value_offset(index + 1), base, blobs, result);
@@ -167,7 +168,7 @@ Status GetValue(PyObject* context, const UnionArray& parent, const Array& arr,
       if (child_name == "tensor") {
         return DeserializeArray(arr, index, base, blobs, result);
       } else if (child_name == "buffer") {
-        int32_t ref = static_cast<const Int32Array&>(arr).Value(index);
+        int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
         *result = wrap_buffer(blobs.buffers[ref]);
         return Status::OK();
       } else {
@@ -180,7 +181,7 @@ Status GetValue(PyObject* context, const UnionArray& parent, const Array& arr,
 }
 
 #define DESERIALIZE_SEQUENCE(CREATE_FN, SET_ITEM_FN)                                     \
-  const auto& data = static_cast<const UnionArray&>(array);                              \
+  const auto& data = checked_cast<const UnionArray&>(array);                             \
   OwnedRef result(CREATE_FN(stop_idx - start_idx));                                      \
   const uint8_t* type_ids = data.raw_type_ids();                                         \
   const int32_t* value_offsets = data.raw_value_offsets();                               \
@@ -215,7 +216,7 @@ Status DeserializeTuple(PyObject* context, const Array& array, int64_t start_idx
 Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
                       int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
                       PyObject** out) {
-  const auto& data = static_cast<const UnionArray&>(array);
+  const auto& data = checked_cast<const UnionArray&>(array);
   OwnedRef result(PySet_New(nullptr));
   const uint8_t* type_ids = data.raw_type_ids();
   const int32_t* value_offsets = data.raw_value_offsets();
diff --git a/cpp/src/arrow/python/builtin_convert.cc b/cpp/src/arrow/python/builtin_convert.cc
index 88674d0..dc0ae8c 100644
--- a/cpp/src/arrow/python/builtin_convert.cc
+++ b/cpp/src/arrow/python/builtin_convert.cc
@@ -31,6 +31,7 @@
 
 #include "arrow/api.h"
 #include "arrow/status.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/logging.h"
 
@@ -351,8 +352,10 @@ template <typename BuilderType>
 class TypedConverter : public SeqConverter {
  public:
   Status Init(ArrayBuilder* builder) override {
-    builder_ = builder;
-    typed_builder_ = static_cast<BuilderType*>(builder);
+    RETURN_NOT_OK(SeqConverter::Init(builder));
+    DCHECK_NE(builder_, nullptr);
+    typed_builder_ = checked_cast<BuilderType*>(builder);
+    DCHECK_NE(typed_builder_, nullptr);
     return Status::OK();
   }
 
@@ -366,7 +369,7 @@ template <typename BuilderType, class Derived>
 class TypedConverterVisitor : public TypedConverter<BuilderType> {
  public:
   Status AppendSingle(PyObject* obj) override {
-    auto self = static_cast<Derived*>(this);
+    auto self = checked_cast<Derived*>(this);
     return self->IsNull(obj) ? self->AppendNull() : self->AppendItem(obj);
   }
 
@@ -374,7 +377,7 @@ class TypedConverterVisitor : public TypedConverter<BuilderType> {
     /// Ensure we've allocated enough space
     RETURN_NOT_OK(this->typed_builder_->Reserve(size));
     // Iterate over the items adding each one
-    auto self = static_cast<Derived*>(this);
+    auto self = checked_cast<Derived*>(this);
     auto visit = [self](PyObject* item) { return self->AppendSingle(item); };
     return internal::VisitSequence(obj, visit);
   }
@@ -480,7 +483,7 @@ class TimestampConverter
         ss << type->ToString();
         return Status::Invalid(ss.str());
       }
-      const TimestampType& ttype = static_cast<const TimestampType&>(*type);
+      const TimestampType& ttype = checked_cast<const TimestampType&>(*type);
       if (unit_ != ttype.unit()) {
         return Status::NotImplemented(
             "Cannot convert NumPy datetime64 objects with differing unit");
@@ -642,7 +645,7 @@ class DecimalConverter
   // Append a non-missing item
   Status AppendItem(PyObject* obj) {
     Decimal128 value;
-    const auto& type = static_cast<const DecimalType&>(*typed_builder_->type());
+    const auto& type = checked_cast<const DecimalType&>(*typed_builder_->type());
     RETURN_NOT_OK(internal::DecimalFromPythonDecimal(obj, type, &value));
     return typed_builder_->Append(value);
   }
@@ -677,7 +680,7 @@ std::unique_ptr<SeqConverter> GetConverter(const std::shared_ptr<DataType>& type
       return std::unique_ptr<SeqConverter>(new Date64Converter);
     case Type::TIMESTAMP:
       return std::unique_ptr<SeqConverter>(
-          new TimestampConverter(static_cast<const TimestampType&>(*type).unit()));
+          new TimestampConverter(checked_cast<const TimestampType&>(*type).unit()));
     case Type::HALF_FLOAT:
       return std::unique_ptr<SeqConverter>(new Float16Converter);
     case Type::FLOAT:
@@ -703,10 +706,10 @@ std::unique_ptr<SeqConverter> GetConverter(const std::shared_ptr<DataType>& type
 
 Status ListConverter::Init(ArrayBuilder* builder) {
   builder_ = builder;
-  typed_builder_ = static_cast<ListBuilder*>(builder);
+  typed_builder_ = checked_cast<ListBuilder*>(builder);
 
   value_converter_ =
-      GetConverter(static_cast<ListType*>(builder->type().get())->value_type());
+      GetConverter(checked_cast<const ListType&>(*builder->type()).value_type());
   if (value_converter_ == nullptr) {
     return Status::NotImplemented("value type not implemented");
   }
@@ -716,19 +719,19 @@ Status ListConverter::Init(ArrayBuilder* builder) {
 
 Status StructConverter::Init(ArrayBuilder* builder) {
   builder_ = builder;
-  typed_builder_ = static_cast<StructBuilder*>(builder);
-  StructType* struct_type = static_cast<StructType*>(builder->type().get());
+  typed_builder_ = checked_cast<StructBuilder*>(builder);
+  const auto& struct_type = checked_cast<const StructType&>(*builder->type());
 
   num_fields_ = typed_builder_->num_fields();
-  DCHECK_EQ(num_fields_, struct_type->num_children());
+  DCHECK_EQ(num_fields_, struct_type.num_children());
 
   field_name_list_.reset(PyList_New(num_fields_));
   RETURN_IF_PYERROR();
 
   // Initialize the child converters and field names
   for (int i = 0; i < num_fields_; i++) {
-    const std::string& field_name(struct_type->child(i)->name());
-    std::shared_ptr<DataType> field_type(struct_type->child(i)->type());
+    const std::string& field_name(struct_type.child(i)->name());
+    std::shared_ptr<DataType> field_type(struct_type.child(i)->type());
 
     auto value_converter = GetConverter(field_type);
     if (value_converter == nullptr) {
diff --git a/cpp/src/arrow/python/helpers.cc b/cpp/src/arrow/python/helpers.cc
index bb0837c..b96d5ff 100644
--- a/cpp/src/arrow/python/helpers.cc
+++ b/cpp/src/arrow/python/helpers.cc
@@ -23,6 +23,7 @@
 #include "arrow/python/common.h"
 #include "arrow/python/decimal.h"
 #include "arrow/python/helpers.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 
 #include <arrow/api.h>
@@ -179,7 +180,7 @@ Status BuilderAppend(FixedSizeBinaryBuilder* builder, PyObject* obj, bool* is_fu
   // XXX For some reason, we must accept unicode objects here
   RETURN_NOT_OK(view.FromString(obj));
   const auto expected_length =
-      static_cast<const FixedSizeBinaryType&>(*builder->type()).byte_width();
+      checked_cast<const FixedSizeBinaryType&>(*builder->type()).byte_width();
   if (ARROW_PREDICT_FALSE(view.size != expected_length)) {
     std::stringstream ss;
     ss << "Got bytestring of length " << view.size << " (expected " << expected_length
diff --git a/cpp/src/arrow/python/numpy_to_arrow.cc b/cpp/src/arrow/python/numpy_to_arrow.cc
index 646fb74..793c318 100644
--- a/cpp/src/arrow/python/numpy_to_arrow.cc
+++ b/cpp/src/arrow/python/numpy_to_arrow.cc
@@ -38,6 +38,7 @@
 #include "arrow/type_fwd.h"
 #include "arrow/type_traits.h"
 #include "arrow/util/bit-util.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/decimal.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/macros.h"
@@ -758,7 +759,7 @@ Status NumPyConverter::ConvertDecimals() {
   Decimal128Builder builder(type_, pool_);
   RETURN_NOT_OK(builder.Resize(length_));
 
-  const auto& decimal_type = static_cast<const DecimalType&>(*type_);
+  const auto& decimal_type = checked_cast<const DecimalType&>(*type_);
 
   for (PyObject* object : objects) {
     const int is_decimal = PyObject_IsInstance(object, decimal_type_.obj());
@@ -983,7 +984,7 @@ Status NumPyConverter::ConvertObjectFixedWidthBytes(
     const std::shared_ptr<DataType>& type) {
   PyAcquireGIL lock;
 
-  const int32_t byte_width = static_cast<const FixedSizeBinaryType&>(*type).byte_width();
+  const int32_t byte_width = checked_cast<const FixedSizeBinaryType&>(*type).byte_width();
 
   // The output type at this point is inconclusive because there may be bytes
   // and unicode mixed in the object array
@@ -1158,7 +1159,7 @@ Status NumPyConverter::ConvertObjects() {
       case Type::DATE64:
         return ConvertDates<Date64Type>();
       case Type::LIST: {
-        const auto& list_field = static_cast<const ListType&>(*type_);
+        const auto& list_field = checked_cast<const ListType&>(*type_);
         return ConvertLists(list_field.value_field()->type());
       }
       case Type::DECIMAL:
@@ -1204,7 +1205,7 @@ inline Status NumPyConverter::ConvertTypedLists(const std::shared_ptr<DataType>&
     have_mask = true;
   }
 
-  BuilderT* value_builder = static_cast<BuilderT*>(builder->value_builder());
+  auto value_builder = checked_cast<BuilderT*>(builder->value_builder());
 
   auto foreach_item = [&](PyObject* object, bool mask) {
     if (mask || internal::PandasObjectIsNull(object)) {
@@ -1249,7 +1250,7 @@ inline Status NumPyConverter::ConvertTypedLists<NPY_OBJECT, NullType>(
     have_mask = true;
   }
 
-  auto value_builder = static_cast<NullBuilder*>(builder->value_builder());
+  auto value_builder = checked_cast<NullBuilder*>(builder->value_builder());
 
   auto foreach_item = [&](PyObject* object, bool mask) {
     if (mask || internal::PandasObjectIsNull(object)) {
@@ -1293,7 +1294,7 @@ inline Status NumPyConverter::ConvertTypedLists<NPY_OBJECT, BinaryType>(
     have_mask = true;
   }
 
-  auto value_builder = static_cast<BinaryBuilder*>(builder->value_builder());
+  auto value_builder = checked_cast<BinaryBuilder*>(builder->value_builder());
 
   auto foreach_item = [&](PyObject* object, bool mask) {
     if (mask || internal::PandasObjectIsNull(object)) {
@@ -1322,7 +1323,7 @@ inline Status NumPyConverter::ConvertTypedLists<NPY_OBJECT, BinaryType>(
         ss << inferred_type->ToString() << " cannot be converted to BINARY.";
         return Status::TypeError(ss.str());
       }
-      return AppendPySequence(object, size, inferred_type, value_builder);
+      return AppendPySequence(object, size, type, value_builder);
     } else {
       return Status::TypeError("Unsupported Python type for list items");
     }
@@ -1346,7 +1347,7 @@ inline Status NumPyConverter::ConvertTypedLists<NPY_OBJECT, StringType>(
     have_mask = true;
   }
 
-  auto value_builder = static_cast<StringBuilder*>(builder->value_builder());
+  auto value_builder = checked_cast<StringBuilder*>(builder->value_builder());
 
   auto foreach_item = [&](PyObject* object, bool mask) {
     if (mask || internal::PandasObjectIsNull(object)) {
@@ -1379,7 +1380,7 @@ inline Status NumPyConverter::ConvertTypedLists<NPY_OBJECT, StringType>(
         ss << inferred_type->ToString() << " cannot be converted to STRING.";
         return Status::TypeError(ss.str());
       }
-      return AppendPySequence(object, size, inferred_type, value_builder);
+      return AppendPySequence(object, size, type, value_builder);
     } else {
       return Status::TypeError("Unsupported Python type for list items");
     }
@@ -1412,8 +1413,8 @@ Status NumPyConverter::ConvertLists(const std::shared_ptr<DataType>& type,
     LIST_CASE(BINARY, NPY_OBJECT, BinaryType)
     LIST_CASE(STRING, NPY_OBJECT, StringType)
     case Type::LIST: {
-      const auto& list_type = static_cast<const ListType&>(*type);
-      auto value_builder = static_cast<ListBuilder*>(builder->value_builder());
+      const auto& list_type = checked_cast<const ListType&>(*type);
+      auto value_builder = checked_cast<ListBuilder*>(builder->value_builder());
 
       auto foreach_item = [this, &builder, &value_builder, &list_type](PyObject* object) {
         if (internal::PandasObjectIsNull(object)) {
@@ -1438,7 +1439,7 @@ Status NumPyConverter::ConvertLists(const std::shared_ptr<DataType>& type,
 Status NumPyConverter::ConvertLists(const std::shared_ptr<DataType>& type) {
   std::unique_ptr<ArrayBuilder> array_builder;
   RETURN_NOT_OK(MakeBuilder(pool_, arrow::list(type), &array_builder));
-  ListBuilder* list_builder = static_cast<ListBuilder*>(array_builder.get());
+  auto list_builder = checked_cast<ListBuilder*>(array_builder.get());
   RETURN_NOT_OK(ConvertLists(type, list_builder, reinterpret_cast<PyObject*>(arr_)));
   return PushBuilderResult(list_builder);
 }
diff --git a/cpp/src/arrow/python/python-test.cc b/cpp/src/arrow/python/python-test.cc
index 81d3fea..60da08b 100644
--- a/cpp/src/arrow/python/python-test.cc
+++ b/cpp/src/arrow/python/python-test.cc
@@ -30,6 +30,7 @@
 #include "arrow/python/builtin_convert.h"
 #include "arrow/python/decimal.h"
 #include "arrow/python/helpers.h"
+#include "arrow/util/checked_cast.h"
 
 namespace arrow {
 namespace py {
@@ -276,7 +277,7 @@ TEST_F(DecimalTest, FromPythonDecimalRescaleNotTruncateable) {
   Decimal128 value;
   OwnedRef python_decimal(this->CreatePythonDecimal("1.001"));
   auto type = ::arrow::decimal(10, 2);
-  const auto& decimal_type = static_cast<const DecimalType&>(*type);
+  const auto& decimal_type = checked_cast<const DecimalType&>(*type);
   ASSERT_RAISES(Invalid, internal::DecimalFromPythonDecimal(python_decimal.obj(),
                                                             decimal_type, &value));
 }
@@ -287,7 +288,7 @@ TEST_F(DecimalTest, FromPythonDecimalRescaleTruncateable) {
   Decimal128 value;
   OwnedRef python_decimal(this->CreatePythonDecimal("1.000"));
   auto type = ::arrow::decimal(10, 2);
-  const auto& decimal_type = static_cast<const DecimalType&>(*type);
+  const auto& decimal_type = checked_cast<const DecimalType&>(*type);
   ASSERT_OK(
       internal::DecimalFromPythonDecimal(python_decimal.obj(), decimal_type, &value));
   ASSERT_EQ(100, value.low_bits());
@@ -297,7 +298,7 @@ TEST_F(DecimalTest, FromPythonNegativeDecimalRescale) {
   Decimal128 value;
   OwnedRef python_decimal(this->CreatePythonDecimal("-1.000"));
   auto type = ::arrow::decimal(10, 9);
-  const auto& decimal_type = static_cast<const DecimalType&>(*type);
+  const auto& decimal_type = checked_cast<const DecimalType&>(*type);
   ASSERT_OK(
       internal::DecimalFromPythonDecimal(python_decimal.obj(), decimal_type, &value));
   ASSERT_EQ(-1000000000, value);
@@ -313,7 +314,7 @@ TEST_F(DecimalTest, TestOverflowFails) {
   ASSERT_EQ(1, metadata.scale());
 
   auto type = ::arrow::decimal(38, 38);
-  const auto& decimal_type = static_cast<const DecimalType&>(*type);
+  const auto& decimal_type = checked_cast<const DecimalType&>(*type);
   ASSERT_RAISES(Invalid, internal::DecimalFromPythonDecimal(python_decimal.obj(),
                                                             decimal_type, &value));
 }
@@ -373,7 +374,7 @@ TEST_F(DecimalTest, TestMixedPrecisionAndScale) {
   MemoryPool* pool = default_memory_pool();
   std::shared_ptr<Array> arr;
   ASSERT_OK(ConvertPySequence(list, pool, &arr));
-  const auto& type = static_cast<const DecimalType&>(*arr->type());
+  const auto& type = checked_cast<const DecimalType&>(*arr->type());
 
   int32_t expected_precision = 9;
   int32_t expected_scale = 3;
@@ -402,7 +403,7 @@ TEST_F(DecimalTest, TestMixedPrecisionAndScaleSequenceConvert) {
 
   ASSERT_OK(ConvertPySequence(list, pool, &arr));
 
-  const auto& type = static_cast<const Decimal128Type&>(*arr->type());
+  const auto& type = checked_cast<const Decimal128Type&>(*arr->type());
   ASSERT_EQ(3, type.precision());
   ASSERT_EQ(3, type.scale());
 }
diff --git a/cpp/src/arrow/status.cc b/cpp/src/arrow/status.cc
index 9b509b4..c2f2f18 100644
--- a/cpp/src/arrow/status.cc
+++ b/cpp/src/arrow/status.cc
@@ -33,7 +33,7 @@ void Status::CopyFrom(const State* state) {
 }
 
 std::string Status::CodeAsString() const {
-  if (state_ == NULL) {
+  if (state_ == nullptr) {
     return "OK";
   }
 
diff --git a/cpp/src/arrow/table_builder-test.cc b/cpp/src/arrow/table_builder-test.cc
index 8167577..32d4bdb 100644
--- a/cpp/src/arrow/table_builder-test.cc
+++ b/cpp/src/arrow/table_builder-test.cc
@@ -29,6 +29,7 @@
 #include "arrow/test-common.h"
 #include "arrow/test-util.h"
 #include "arrow/type.h"
+#include "arrow/util/checked_cast.h"
 
 namespace arrow {
 
@@ -58,7 +59,7 @@ void AppendValues(BuilderType* builder, const std::vector<T>& values,
 template <typename ValueType, typename T>
 void AppendList(ListBuilder* builder, const std::vector<std::vector<T>>& values,
                 const std::vector<bool>& is_valid) {
-  auto values_builder = static_cast<ValueType*>(builder->value_builder());
+  auto values_builder = checked_cast<ValueType*>(builder->value_builder());
 
   for (size_t i = 0; i < values.size(); ++i) {
     if (is_valid.size() == 0 || is_valid[i]) {
@@ -108,7 +109,7 @@ TEST_F(TestRecordBatchBuilder, Basics) {
   const int kIter = 3;
   for (int i = 0; i < kIter; ++i) {
     AppendData(builder->GetFieldAs<Int32Builder>(0),
-               static_cast<StringBuilder*>(builder->GetField(1)),
+               checked_cast<StringBuilder*>(builder->GetField(1)),
                builder->GetFieldAs<ListBuilder>(2));
 
     std::shared_ptr<RecordBatch> batch;
diff --git a/cpp/src/arrow/table_builder.h b/cpp/src/arrow/table_builder.h
index 582389b..8a13f4d 100644
--- a/cpp/src/arrow/table_builder.h
+++ b/cpp/src/arrow/table_builder.h
@@ -25,6 +25,7 @@
 
 #include "arrow/status.h"
 #include "arrow/type.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/visibility.h"
 
 namespace arrow {
@@ -65,7 +66,7 @@ class RecordBatchBuilder {
   /// \return pointer to template type
   template <typename T>
   T* GetFieldAs(int i) {
-    return static_cast<T*>(raw_field_builders_[i]);
+    return checked_cast<T*>(raw_field_builders_[i]);
   }
 
   /// \brief Finish current batch and optionally reset
diff --git a/cpp/src/arrow/tensor.cc b/cpp/src/arrow/tensor.cc
index 465f724..b0b2b4b 100644
--- a/cpp/src/arrow/tensor.cc
+++ b/cpp/src/arrow/tensor.cc
@@ -27,6 +27,7 @@
 #include "arrow/compare.h"
 #include "arrow/status.h"
 #include "arrow/type.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/logging.h"
 
 namespace arrow {
@@ -73,7 +74,7 @@ Tensor::Tensor(const std::shared_ptr<DataType>& type, const std::shared_ptr<Buff
     : type_(type), data_(data), shape_(shape), strides_(strides), dim_names_(dim_names) {
   DCHECK(is_tensor_supported(type->id()));
   if (shape.size() > 0 && strides.size() == 0) {
-    ComputeRowMajorStrides(static_cast<const FixedWidthType&>(*type_), shape, &strides_);
+    ComputeRowMajorStrides(checked_cast<const FixedWidthType&>(*type_), shape, &strides_);
   }
 }
 
@@ -103,14 +104,14 @@ bool Tensor::is_contiguous() const { return is_row_major() || is_column_major();
 
 bool Tensor::is_row_major() const {
   std::vector<int64_t> c_strides;
-  const auto& fw_type = static_cast<const FixedWidthType&>(*type_);
+  const auto& fw_type = checked_cast<const FixedWidthType&>(*type_);
   ComputeRowMajorStrides(fw_type, shape_, &c_strides);
   return strides_ == c_strides;
 }
 
 bool Tensor::is_column_major() const {
   std::vector<int64_t> f_strides;
-  const auto& fw_type = static_cast<const FixedWidthType&>(*type_);
+  const auto& fw_type = checked_cast<const FixedWidthType&>(*type_);
   ComputeColumnMajorStrides(fw_type, shape_, &f_strides);
   return strides_ == f_strides;
 }
diff --git a/cpp/src/arrow/type-test.cc b/cpp/src/arrow/type-test.cc
index e9245d5..542e361 100644
--- a/cpp/src/arrow/type-test.cc
+++ b/cpp/src/arrow/type-test.cc
@@ -26,6 +26,7 @@
 
 #include "arrow/test-util.h"
 #include "arrow/type.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/key_value_metadata.h"
 
 using std::shared_ptr;
@@ -309,8 +310,8 @@ TEST(TestDateTypes, Attrs) {
   ASSERT_EQ("date32[day]", t1->ToString());
   ASSERT_EQ("date64[ms]", t2->ToString());
 
-  ASSERT_EQ(32, static_cast<const FixedWidthType&>(*t1).bit_width());
-  ASSERT_EQ(64, static_cast<const FixedWidthType&>(*t2).bit_width());
+  ASSERT_EQ(32, checked_cast<const FixedWidthType&>(*t1).bit_width());
+  ASSERT_EQ(64, checked_cast<const FixedWidthType&>(*t2).bit_width());
 }
 
 TEST(TestTimeType, Equals) {
diff --git a/cpp/src/arrow/type.cc b/cpp/src/arrow/type.cc
index 2f6e718..ba84059 100644
--- a/cpp/src/arrow/type.cc
+++ b/cpp/src/arrow/type.cc
@@ -26,6 +26,7 @@
 #include "arrow/array.h"
 #include "arrow/compare.h"
 #include "arrow/status.h"
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/key_value_metadata.h"
 #include "arrow/util/logging.h"
 #include "arrow/util/stl.h"
@@ -255,7 +256,7 @@ DictionaryType::DictionaryType(const std::shared_ptr<DataType>& index_type,
       ordered_(ordered) {}
 
 int DictionaryType::bit_width() const {
-  return static_cast<const FixedWidthType*>(index_type_.get())->bit_width();
+  return checked_cast<const FixedWidthType&>(*index_type_).bit_width();
 }
 
 std::shared_ptr<Array> DictionaryType::dictionary() const { return dictionary_; }
diff --git a/cpp/src/arrow/type.h b/cpp/src/arrow/type.h
index 915c0c7..2c5ec31 100644
--- a/cpp/src/arrow/type.h
+++ b/cpp/src/arrow/type.h
@@ -28,6 +28,7 @@
 
 #include "arrow/status.h"
 #include "arrow/type_fwd.h"  // IWYU pragma: export
+#include "arrow/util/checked_cast.h"
 #include "arrow/util/key_value_metadata.h"
 #include "arrow/util/macros.h"
 #include "arrow/util/visibility.h"
@@ -274,7 +275,7 @@ class ARROW_EXPORT CTypeImpl : public BASE {
   int bit_width() const override { return static_cast<int>(sizeof(C_TYPE) * CHAR_BIT); }
 
   Status Accept(TypeVisitor* visitor) const override {
-    return visitor->Visit(*static_cast<const DERIVED*>(this));
+    return visitor->Visit(checked_cast<const DERIVED&>(*this));
   }
 
   std::string ToString() const override { return this->name(); }
diff --git a/cpp/src/arrow/util/CMakeLists.txt b/cpp/src/arrow/util/CMakeLists.txt
index 8969527..36cbd6d 100644
--- a/cpp/src/arrow/util/CMakeLists.txt
+++ b/cpp/src/arrow/util/CMakeLists.txt
@@ -53,6 +53,7 @@ if (ARROW_BUILD_BENCHMARKS)
 endif()
 
 ADD_ARROW_TEST(bit-util-test)
+ADD_ARROW_TEST(checked-cast-test)
 ADD_ARROW_TEST(compression-test)
 ADD_ARROW_TEST(decimal-test)
 ADD_ARROW_TEST(key-value-metadata-test)
diff --git a/cpp/src/arrow/util/checked-cast-test.cc b/cpp/src/arrow/util/checked-cast-test.cc
new file mode 100644
index 0000000..4dc8438
--- /dev/null
+++ b/cpp/src/arrow/util/checked-cast-test.cc
@@ -0,0 +1,71 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <type_traits>
+
+#include <gtest/gtest.h>
+
+#include "arrow/util/checked_cast.h"
+
+namespace arrow {
+
+class Foo {
+ public:
+  virtual ~Foo() = default;
+};
+
+class Bar {};
+class FooSub : public Foo {};
+template <typename T>
+class Baz : public Foo {};
+
+TEST(CheckedCast, TestInvalidSubclassCast) {
+  static_assert(std::is_polymorphic<Foo>::value, "Foo is not polymorphic");
+
+  Foo foo;
+  FooSub foosub;
+  const Foo& foosubref = foosub;
+  Baz<double> baz;
+  const Foo& bazref = baz;
+
+#ifndef NDEBUG  // debug mode
+  // illegal pointer cast
+  ASSERT_EQ(nullptr, checked_cast<Bar*>(&foo));
+
+  // illegal reference cast
+  ASSERT_THROW(checked_cast<const Bar&>(foosubref), std::bad_cast);
+
+  // legal reference casts
+  ASSERT_NO_THROW(checked_cast<const FooSub&>(foosubref));
+  ASSERT_NO_THROW(checked_cast<const Baz<double>&>(bazref));
+#else  // release mode
+  // failure modes for the invalid casts occur at compile time
+
+  // legal pointer cast
+  ASSERT_NE(nullptr, checked_cast<const FooSub*>(&foosubref));
+
+  // legal reference casts: this is static_cast in a release build, so ASSERT_NO_THROW
+  // doesn't make a whole lot of sense here.
+  auto& x = checked_cast<const FooSub&>(foosubref);
+  ASSERT_EQ(&foosubref, &x);
+
+  auto& y = checked_cast<const Baz<double>&>(bazref);
+  ASSERT_EQ(&bazref, &y);
+#endif
+}
+
+}  // namespace arrow
diff --git a/cpp/src/arrow/util/hash.cc b/cpp/src/arrow/util/checked_cast.h
similarity index 54%
copy from cpp/src/arrow/util/hash.cc
copy to cpp/src/arrow/util/checked_cast.h
index 94ba524..d3714d8 100644
--- a/cpp/src/arrow/util/hash.cc
+++ b/cpp/src/arrow/util/checked_cast.h
@@ -15,24 +15,28 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#include "arrow/util/hash.h"
+#ifndef ARROW_CAST_H
+#define ARROW_CAST_H
 
-#include "arrow/buffer.h"
-#include "arrow/status.h"
+#include <type_traits>
 
 namespace arrow {
-namespace internal {
 
-Status NewHashTable(int64_t size, MemoryPool* pool, std::shared_ptr<Buffer>* out) {
-  auto hash_table = std::make_shared<PoolBuffer>(pool);
-
-  RETURN_NOT_OK(hash_table->Resize(sizeof(hash_slot_t) * size));
-  int32_t* slots = reinterpret_cast<hash_slot_t*>(hash_table->mutable_data());
-  std::fill(slots, slots + size, kHashSlotEmpty);
-
-  *out = hash_table;
-  return Status::OK();
+template <typename OutputType, typename InputType>
+inline OutputType checked_cast(InputType&& value) {
+  static_assert(std::is_class<typename std::remove_pointer<
+                    typename std::remove_reference<InputType>::type>::type>::value,
+                "checked_cast input type must be a class");
+  static_assert(std::is_class<typename std::remove_pointer<
+                    typename std::remove_reference<OutputType>::type>::type>::value,
+                "checked_cast output type must be a class");
+#ifdef NDEBUG
+  return static_cast<OutputType>(value);
+#else
+  return dynamic_cast<OutputType>(value);
+#endif
 }
 
-}  // namespace internal
 }  // namespace arrow
+
+#endif  // ARROW_CAST_H
diff --git a/cpp/src/arrow/util/hash.cc b/cpp/src/arrow/util/hash.cc
index 94ba524..4f2f1a2 100644
--- a/cpp/src/arrow/util/hash.cc
+++ b/cpp/src/arrow/util/hash.cc
@@ -27,7 +27,7 @@ Status NewHashTable(int64_t size, MemoryPool* pool, std::shared_ptr<Buffer>* out
   auto hash_table = std::make_shared<PoolBuffer>(pool);
 
   RETURN_NOT_OK(hash_table->Resize(sizeof(hash_slot_t) * size));
-  int32_t* slots = reinterpret_cast<hash_slot_t*>(hash_table->mutable_data());
+  auto slots = reinterpret_cast<hash_slot_t*>(hash_table->mutable_data());
   std::fill(slots, slots + size, kHashSlotEmpty);
 
   *out = hash_table;
diff --git a/cpp/src/arrow/visitor_inline.h b/cpp/src/arrow/visitor_inline.h
index 41b0108..ae5307b 100644
--- a/cpp/src/arrow/visitor_inline.h
+++ b/cpp/src/arrow/visitor_inline.h
@@ -24,12 +24,13 @@
 #include "arrow/status.h"
 #include "arrow/tensor.h"
 #include "arrow/type.h"
+#include "arrow/util/checked_cast.h"
 
 namespace arrow {
 
 #define TYPE_VISIT_INLINE(TYPE_CLASS) \
   case TYPE_CLASS::type_id:           \
-    return visitor->Visit(static_cast<const TYPE_CLASS&>(type));
+    return visitor->Visit(checked_cast<const TYPE_CLASS&>(type));
 
 template <typename VISITOR>
 inline Status VisitTypeInline(const DataType& type, VISITOR* visitor) {
@@ -71,7 +72,7 @@ inline Status VisitTypeInline(const DataType& type, VISITOR* visitor) {
 #define ARRAY_VISIT_INLINE(TYPE_CLASS) \
   case TYPE_CLASS::type_id:            \
     return visitor->Visit(             \
-        static_cast<const typename TypeTraits<TYPE_CLASS>::ArrayType&>(array));
+        checked_cast<const typename TypeTraits<TYPE_CLASS>::ArrayType&>(array));
 
 template <typename VISITOR>
 inline Status VisitArrayInline(const Array& array, VISITOR* visitor) {
diff --git a/python/pyarrow/tests/pandas_examples.py b/python/pyarrow/tests/pandas_examples.py
index f11da3c..65791d4 100644
--- a/python/pyarrow/tests/pandas_examples.py
+++ b/python/pyarrow/tests/pandas_examples.py
@@ -98,7 +98,7 @@ def dataframe_with_lists(include_index=False):
         [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
         [0, 1, 2, 3, 4],
         None,
-        [0],
+        [],
         np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9] * 2,
                  dtype=np.int64)[::2]
     ]
@@ -107,7 +107,7 @@ def dataframe_with_lists(include_index=False):
         [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.],
         [0., 1., 2., 3., 4.],
         None,
-        [0.],
+        [],
         np.array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] * 2)[::2],
     ]
     fields.append(pa.field('bytes_list', pa.list_(pa.binary())))

-- 
To stop receiving notification emails like this one, please contact
uwe@apache.org.