Rails Rails 中使用 Hash 的小陷阱,不知道各位注意到没有

ZhongWen_Zhou · 2013年04月25日 · 最后由 ZhongWen_Zhou 回复于 2013年04月25日 · 4422 次阅读

背景:通过表单传来的参数,我们可以通过 params[:arg_name] 取得其值,但是偶尔需要 merge 一些新的东西,params = {:other_arg => value}.merge(params),此时再用 params[:arg_name] 便无法取得原来的值,后来经过一番折腾,发现如果改为:params = params.merge({:other_arg => value}),然后通过 params[:arg_name] 就可以取得值,对于这个非常诡异的问题,有如下陷阱,各位不要再掉进去咯

原来 Rails 再帮我们处理参数的时候,会将 params 转换为 Object::HashWithIndifferentAccess 这样的类型,而这一个类型对于 key 是既可以用 string 也可以用 symbol 的,而这一类型的实例再 merge 一个 hash 对象时,也同样会转换成 Object::HashWithIndifferentAccess 类型。

相反的,我们的第一种写法,{:other_arg => value}.merge(params),这里的{:other_arg => xxx}本身是一个 ruby 的 hash 对象,不是 Object::HashWithIndifferentAccess 的类型,所以当它再 merge 一个 Object::HashWithIndifferentAccess < Hash 对象时,它只能转型成 ruby 的 hash 对象,而对于 ruby 的 hash 对象,大家都知道 key 是区分 string 和 symbol 的,因此出现了上述问题

params[:arg_name] # => get value
# do something if you like
params.merge!({:other_arg => value})
params[:arg_name] # => can get value

#1 楼 @ywjno 你是在 rails 应用里面测的,还是直接 irb 这样测的!?我说的是在 rails 里面,特别是针对 controller 中取得参数然后 merge 的这种情况,可以试试哦

楼主无论params = {:other_arg => value}.merge(params)还是params = params.merge({:other_arg => value})都是错误的 1 楼的params.merge!({:other_arg => value})是正确的 因为 params 是一个方法而不是 local variable,楼主不该把它变成 local variable(就算要赋值,也该用self.params =来 callparams=方法),而且确实就如楼主所言,连类型都改掉了,出 Bug 一点不奇怪。

#3 楼 @iBachue 嗯,是的,只是自己折腾半天,顺便提醒一下

需要 登录 后方可回复, 如果你还没有账号请 注册新账号