-
-
Notifications
You must be signed in to change notification settings - Fork 450
Description
Expr fails to automatically dereference pointer fields when using them as keys for map literal access, even though it successfully dereferences pointers in other expression contexts. In my use case, I tried to use expr expressions in a yaml file to map two different related but very different structs to each other (a forward translation and a reverse translation). My yaml file consisted of field names of my target struct as keys and the expr expression as the values. It mostly worked very well until I moved from proof of concept to real mappings created by business analysts. Besides mapping keys to other keys, we also need to translate the equivalent values in one system to values in another system. I tried to help with if/else (not implemented) and then nested if statements (not supported), multiple sequential if statements (not supported), and finally, maps (couldn't dereference the values of my structs because all fields in them are pointers).
Minimal Reproduction
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
type Config struct {
Level *string
}
func main() {
level := "info"
config := Config{Level: &level}
env := map[string]interface{}{
"config": config,
}
// This works fine - pointer is automatically dereferenced
program1, err := expr.Compile(`config.Level == "info"`, expr.Env(env))
if err != nil {
panic(err)
}
result1, _ := expr.Run(program1, env)
fmt.Printf("Comparison works: %v\n", result1) // true
// This fails - cannot use pointer as map key
_, err = expr.Compile(`{"debug": "DEBUG", "info": "INFO", "warn": "WARN"}[config.Level]`, expr.Env(env))
if err != nil {
fmt.Printf("Map access fails: %v\n", err)
// Error: cannot use *string to get an element from map[string]interface {}
}
}
Expected behavior: The pointer should be automatically dereferenced for map access, just like it is for comparisons and other operations. If it's nil in this case, then the returned value should probably be nil too.
Actual behavior: Compilation fails with error: cannot use *string to get an element from map[string]interface{}
Impact
This inconsistency forces users to work around the limitation by:
- Using complex ternary chains instead of clean map lookups. This would be alleviated somewhat if we coudl have multiple if statements in an expr, nested statements, or if "else if" was supported, though both fixes are probably important.
- Pre-processing data to dereference pointers manually (needs an extra type for everything used in expr which is probably not practical).
- Avoiding pointer fields in structs used with expr (not practical as DTO or database structs or plenty of other places might need pointers).
Map literals are a clean, readable way to handle value mappings, especially for non-technical users writing configuration expressions. The inability to use pointer fields with maps significantly reduces expr's usability in real-world Go applications where pointer fields are common. It also feels like a very functional approach that jives with the intent of expr
Proposed Solution
Automatically dereference pointer fields when used as map keys, consistent with how pointers are handled in comparisons, function calls, and other expression contexts.
Environment
expr version: 1.17.5
Go version: 1.24.4