diff --git a/cpp/src/arrow/type.h b/cpp/src/arrow/type.h index e3582056ead..e98d7295850 100644 --- a/cpp/src/arrow/type.h +++ b/cpp/src/arrow/type.h @@ -150,7 +150,12 @@ class ARROW_EXPORT DataType : public std::enable_shared_from_this, bool Equals(const std::shared_ptr& other, bool check_metadata = false) const; /// \brief Return the child field at index i. - const std::shared_ptr& field(int i) const { return children_[i]; } + /// + /// Returns a null shared_ptr if index is out of bounds [0, num_fields()). + const std::shared_ptr& field(int i) const { + static const std::shared_ptr kNullField; + return (i < 0 || i >= num_fields()) ? kNullField : children_[i]; + } /// \brief Return the children fields associated with this type. const FieldVector& fields() const { return children_; } diff --git a/cpp/src/arrow/type_test.cc b/cpp/src/arrow/type_test.cc index e9b1d30e6e7..ae34f966616 100644 --- a/cpp/src/arrow/type_test.cc +++ b/cpp/src/arrow/type_test.cc @@ -2029,8 +2029,27 @@ TEST(TestStructType, Basics) { auto t3 = struct_({field("c", int8()), field("b", utf8())}); ASSERT_TRUE(t1->Equals(t2)); ASSERT_TRUE(!t1->Equals(t3)); +} + +// Test that out-of-bounds field access returns null +TEST(TestStructType, FieldAccessOutOfBounds) { + auto f0 = field("f0", int32()); + auto f1 = field("f1", utf8()); + auto f2 = field("f2", uint8()); + StructType struct_type({f0, f1, f2}); + + ASSERT_EQ(struct_type.num_fields(), 3); + + // Out-of-bounds indices return nullptr + ASSERT_EQ(struct_type.field(3), nullptr); + ASSERT_EQ(struct_type.field(4), nullptr); + ASSERT_EQ(struct_type.field(100), nullptr); + ASSERT_EQ(struct_type.field(-1), nullptr); - // TODO(wesm): out of bounds for field(...) + // All out-of-bounds accesses return the same static null + const auto& null1 = struct_type.field(3); + const auto& null2 = struct_type.field(100); + EXPECT_EQ(&null1, &null2); } TEST(TestStructType, GetFieldByName) {