Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(container): Add NilChecker Support to gmap, gset, and gtree for Typed Nil Issue Resolution#4605

Merged
hailaz merged 4 commits intogogf:masterfrom
LanceAdd:fix/isnil
Jan 15, 2026
Merged

fix(container): Add NilChecker Support to gmap, gset, and gtree for Typed Nil Issue Resolution#4605
hailaz merged 4 commits intogogf:masterfrom
LanceAdd:fix/isnil

Conversation

@LanceAdd
Copy link
Member

描述

本PR为gmapgsetgtree容器引入了NilChecker机制,以解决Go语言中的typed nil问题。该实现允许用户注册自定义的nil检查函数来确定值是否应被视为nil,这对于处理那些会被存储到容器中的typed nil值特别有用。

情况描述

当前gmap等容器的泛型容器存在对valuenil值无法正确过滤的问题,例如以下例子中如果使用默认的if any(value) != nil去判断就会得到错误的结果,原因是会出现带有类型的(*Student)(nil)直接和nil比较或者使用any强转都是不对的,使用反射可以解决但是性能太差了,所以换个思虑我们让用户自己决定如何判断nil就能解决这个问题

func main() {
	type Student struct {
		Name string
		Age  int
	}
	m1 := gmap.NewKVMap[int, *Student](true)
	for i := 0; i < 10; i++ {
		m1.GetOrSetFuncLock(i, func() *Student {
			if i%2 == 0 {
				return &Student{}
			}
			return nil
		})
	}
	fmt.Println(m1.Size()) //  10
	m2 := gmap.NewKVMap[int, *Student](true)
	m2.RegisterNilChecker(func(student *Student) bool {
		return student == nil
	})
	for i := 0; i < 10; i++ {
		m2.GetOrSetFuncLock(i, func() *Student {
			if i%2 == 0 {
				return &Student{}
			}
			return nil
		})
	}
	fmt.Println(m2.Size())  // 5

}

变更内容

  • 在gmap、gset和gtree包中添加了NilChecker类型定义
  • 扩展容器结构体,增加nilChecker字段来存储自定义nil检查函数
  • 实现了RegisterNilChecker方法,允许用户注册自定义nil检查逻辑
  • 添加了isNil内部方法,优先使用自定义nil检查函数或回退到默认的any(v) == nil检查
  • 更新关键操作(AddIfNotExist、Set等)以利用nil检查机制
  • 为所有三个容器类型添加了全面的测试用例以验证nilchecker功能

LanceAdd and others added 3 commits January 14, 2026 12:02
- 在KVMap、ListKVMap、TSet和各种树结构中引入NilChecker类型
- 实现RegisterNilChecker方法用于注册自定义nil检查函数
- 添加isNil方法统一处理nil值判断逻辑
- 修改GetOrSet、AddIfNotExist等相关方法使用新的nil检查机制
- 为所有支持的数据结构添加相应的单元测试验证功能正确性
- 更新红黑树比较器调用方式以支持泛型参数传递
@LanceAdd LanceAdd added the bug It is confirmed a bug, but don't worry, we'll handle it. label Jan 14, 2026
@LanceAdd LanceAdd changed the title fix(container/gmap/gset/gtree): Add NilChecker Support to gmap, gset, and gtree for Typed Nil Issue Resolution fix(container): Add NilChecker Support to gmap, gset, and gtree for Typed Nil Issue Resolution Jan 14, 2026
@hailaz hailaz requested a review from Copilot January 15, 2026 01:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces a NilChecker mechanism to the gmap, gset, and gtree containers to address the typed nil problem in Go. The feature allows users to register custom nil checker functions that determine whether a value should be treated as nil, solving the issue where typed nil pointers (e.g., (*Student)(nil)) are not correctly identified when using the standard any(value) == nil check.

Changes:

  • Added NilChecker type and registration methods across gmap, gset, and gtree packages
  • Updated nil-checking logic in Set/GetOrSet operations to use custom checkers when available
  • Added comprehensive test coverage for the new functionality across all three container types

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
container/gmap/gmap_hash_k_v_map.go Added NilChecker type, registration method, and updated nil checks in GetOrSetFuncLock and doSetWithLockCheck
container/gmap/gmap_list_k_v_map.go Added nilChecker field and updated nil checks in GetOrSetFuncLock and SetIfNotExist* methods
container/gmap/gmap_z_unit_k_v_map_test.go Added test for typed nil handling with custom nil checker
container/gmap/gmap_z_unit_list_k_v_map_test.go Added test for typed nil handling with custom nil checker
container/gset/gset_t_set.go Added NilChecker type, registration method, and updated nil checks in AddIfNotExist* methods
container/gset/gset_z_unit_t_set_test.go Added test for typed nil handling with custom nil checker
container/gtree/gtree_k_v_avltree.go Added NilChecker type, registration method, and updated nil check in doSet
container/gtree/gtree_k_v_btree.go Added nilChecker field and updated nil check in doSet
container/gtree/gtree_k_v_redblacktree.go Added nilChecker field and updated nil check in doSet
container/gtree/gtree_redblacktree.go Fixed comparator call to use generic version
container/gtree/gtree_z_k_v_tree_test.go Added tests for typed nil handling across AVL, B-tree, and Red-Black tree implementations
Comments suppressed due to low confidence (3)

container/gmap/gmap_hash_k_v_map.go:324

  • The SetIfNotExist method does not utilize the nil checker mechanism. When the value is nil (according to the custom nil checker), it should not be set in the map. This is inconsistent with other methods like doSetWithLockCheck and GetOrSetFuncLock. Add an isNil check before setting the value to maintain consistency with the PR's goal of filtering typed nil values.
func (m *KVMap[K, V]) SetIfNotExist(key K, value V) bool {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.data == nil {
		m.data = make(map[K]V)
	}
	if _, ok := m.data[key]; !ok {
		m.data[key] = value
		return true
	}
	return false
}

container/gmap/gmap_hash_k_v_map.go:351

  • The SetIfNotExistFuncLock method does not utilize the nil checker mechanism. Similar to SetIfNotExist, it should check if the value returned by f() is nil (according to the custom nil checker) before setting it in the map. This creates inconsistent behavior compared to GetOrSetFuncLock and other methods that were updated in this PR.
func (m *KVMap[K, V]) SetIfNotExistFuncLock(key K, f func() V) bool {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.data == nil {
		m.data = make(map[K]V)
	}
	if _, ok := m.data[key]; !ok {
		m.data[key] = f()
		return true
	}
	return false
}

container/gmap/gmap_hash_k_v_map.go:333

  • The SetIfNotExistFunc method does not check for nil values. While it delegates to SetIfNotExist, which also doesn't check for nil, this creates a gap in the nil checker implementation. Consider evaluating f() and checking isNil before calling SetIfNotExist to ensure consistent behavior with the pattern established in ListKVMap.
func (m *KVMap[K, V]) SetIfNotExistFunc(key K, f func() V) bool {
	if !m.Contains(key) {
		return m.SetIfNotExist(key, f())
	}
	return false
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@hailaz hailaz merged commit 13524a3 into gogf:master Jan 15, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug It is confirmed a bug, but don't worry, we'll handle it.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants