7 ms·
Ruby Symbols
- Mystery-Machine 11mo agoSymbols are a foot gun. > Symbols aren’t interchangeable though. user = { name: "Alice", age: 30 } puts user[:name] # Alice puts user["name"] # nil I'm 100% convinced that every Ruby developer has at least once made a bug where they tried to access a hash entry using a symbol, where the key was actually a string or vice-versa. It would be great if Ruby would finally have immutable strings by default and, at that point, it would be possible to make symbols be strings. This would prevent any such user[:name] vs user["name"] bugs while not breaking any other functionality. And also keeping the memory "optimized" by reusing a single immutable string.
- Lio 11mo agoSure, we wouldn't have ActiveSupport::HashWithIndifferentAccess if it wasn't an occasional issue.
- shevy-java 11mo agoAgreed. However had it should also be mentioned that this originated from rails. Many bad things originate from the rails ecosystem. (Arguably some good too, but I am very pessimistic ever since shopify's recent powermove and DHH side-line commenting off-the-fence while being on shopify's board.)
- Lio 11mo agoRails has paid my salary for the best part of 20 years on and off. I'm OK with it. ;)
- byroot 11mo agoHashWithIndifferentAccess was added because back in the day symbols were immortal, hence could not be used for request parameters. It no longer make sense today, and any new use of it is a smell.
- shevy-java 11mo ago> I'm 100% convinced that every Ruby developer has at least once made a bug where they tried to access a hash entry using a symbol, where the key was actually a string or vice-versa. Yeah, that is true. It adds a cognitive load onto the ruby developer writing the code as well. Personally I prefer symbols as keys in a Hash, mostly because I like symbols, I assume it may be faster usually (this depends on factors, such as how many symbols one uses, the garbage collection kicking off and so forth, but by and large I think for most use cases, Symbols are simply more efficient). We also have abominations such as HashWithIndifferentAccess; Jeremy wrote an article why that is not good (indirectly, e. g. the article he wrote was about Symbols more, their use cases and differences to Strings, but from this it follows that HashWithIndifferentAccess is not a good idea. While I agree, I think some people simply don't want to have to care either way). If I need to query a hash often, I tend to write a method, and the method then makes sure any input is either a string or a symbol for that given Hash. > It would be great if Ruby would finally have immutable strings by default But it has. I still use "# frozen_string_literal: true", but if you omit it, the Strings are frozen by default. People could set "# frozen_string_literal: true" in a .rb file if they want to retain the old behaviour. > it would be possible to make symbols be strings. But Symbols are not Strings. And bugs based on x[:foo] versus x['foo'] are always going to happen. They are very easy to avoid though. I don't really run into these in my own code, largely because I settled on symbols as keys for a Hash. > And also keeping the memory "optimized" by reusing a single immutable string. But a Symbol is not a String. Not even an immutable String. I understand what you mean (and internally it may be that way already, actually), but it is not a String.
- Mystery-Machine 11mo agoI also prefer symbols as keys in hash. It just looks more aesthetically pleasing. :) I think the optimization string vs symbol is negligent in most of the apps. If you need that level of optimization, you should probably switch to Rust. > If I need to query a hash often, I tend to write a method, and the method then makes sure any input is either a string or a symbol for that given Hash. This is terrible. This is the exact opposite of what Ruby is trying to achieve: developer happiness. You basically implement "symbol is a string" for hashes (aka HashWithIndifferentAccess). > But it has. I still use "# frozen_string_literal: true", but if you omit it, the Strings are frozen by default. This is not the case. If you omit "# frozen_string_literal: true", the strings are mutable, in all versions of Ruby, even in Ruby 4.0, which will be released on 25 Dec. > But a Symbol is not a String. Not even an immutable String. I understand what you mean (and internally it may be that way already, actually), but it is not a String. If it walks like a duck and quacks like a duck... Who cares? What's the difference it makes for you whether symbols and string are interchangeable? Show me one valid use-case where having symbols and strings being different (user[:name] vs user["name"], or attr_reader "name") is useful.
- an0malous 11mo agoThat’s not much more of a foot gun than: user[“1”] = user[1] Symbols are always taught as a first class object in Ruby, not just syntactic sugar for accessing hashes. “foo” does not equal :foo
- IshKebab 11mo agoThe foot-gun there is dynamic typing, not symbols.
- thomascountz 11mo agoHere's a fantastic writeup about frozen strings in Ruby and the upcoming changes: https://byroot.github.io/ruby/performance/2025/10/28/string-literals.html https://byroot.github.io/ruby/performance/2025/10/28/string-...
- tsm 11mo agoI'd guess that the majority of people who've made a bug like this got started on Ruby via Rails, where many hashes are HashWithIndifferentAccesses. HWIAs are convenient, but they do confuse the issue.
- rubyfan 11mo agoRails makes this more confusing with HashWithIndifferentAccess[1]. People coming from Rails are often confused by this when working with straight ruby and this kind of hash access doesn’t work. 1. https://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html https://api.rubyonrails.org/classes/ActiveSupport/HashWithIn...
- MRson 11mo ago[dead]
- masklinn 11mo agoOP is really tying themselves into knots. Symbols are special cased pseudo-strings for langages which have either extremely inefficient strings (erlang) or mutable ones (ruby). That’s about the extent of it. Python or Java don’t really have a use for them because their strings are immutable, and likely interned when metaprogramming is relevant (e.g. method or attribute names).
- raincole 11mo agoSymbol is the only feature I miss after switching to Python. It makes code so much more readable to distinguish keys and normal strings.
- masklinn 11mo agoMuch like yccs27 above, I do that using single and double quoted strings (a habit I got from Erlang).
- Joker_vD 11mo agoWhat about LISPs? They have atoms too.
- __s 11mo agoGenerally mutable strings there too, like Ruby
- bashkiddie 11mo agoCould not read OP, clownflare is down. > Symbols are pseudo-strings Can guess by LISP: Symbols reside in the symbol table, which is global scope. Strings reside in the variable tables, subject to local scope. It is two different storage mechanisms > inefficient strings Ruby does not care for efficiency, so there is no point to argue for symbols vs string performance
- masklinn 11mo ago> It is two different storage mechanisms An irrelevant implementation detail. Interned strings are also stored globally, and depending on implementations interned strings may or may not be subject to memory reclaiming. > Ruby does not care for efficiency, so there is no point to argue for symbols vs string performance Which is why Ruby's having symbols is associated with mutable strings, not inefficient strings. And there's a gulf between not caring too much about efficiency and strings being a linked list of integers, which is what they are in Erlang.
- Ecco 11mo agoI think the whole article is super confusing. A Symbol is really just a string! Well, it's a string that will guarantee unique allocations (two identical strings are guaranteed to be allocated at the same address), which makes equality checks super fast (compare pointers directly). But pretty much just a string nonetheless...
- lloeki 11mo agoA symbol really is just an integer which happens to have a fancy name when looked at from a user's point of view.
- kazinator 11mo agoLisp symbols have various properties; the exact set depends on the dialect. The properties may be mutable (e.g. property list, value cell). You can certainly associate such things with an integer: e.g. value_cell[42], plist[42]. But those associations are part of the "symbolness", not just the 42. Integers are not suitable symbols in some ways: they have a security problem. Real symbols can be used for sandboxing: if you don't already hold the symbol, you have no way to get it. This won't happen for integers. Especially not reasonably small integers. What do I mean that if you don't already have a symbol, you have no way to get it? If the symbol is interned, you can intern it, right? Not if there is a symbol package system, and you don't have access to the package in which the symbol is interned. Now you might say, since objects are typically represented as pointers, aren't those a kind of integer anyway? Yes they are, but they are different type which doesn't support arithmetic; we can restrict programs from calculating arbitrary pointers, but we can't restrict programs from calculating arbitrary integers. Even if we have escape hatches for converting an integer to a pointer, or other security/safety bypassng mechanisms, those escape hatches have an API which is identified by symbols, which we can exclude from the sandbox.
- lloeki 11mo agoI (and GP given their use of capitalised Symbol) was talking about Ruby symbols, which for the most part are more equivalent to their object_id than to their textual representation. What I mean by "integer" is not "pointer", it's "natural number", in the sense that there exists only one `1` vs you can have multiple textual "foo" strings. So it's more useful to think of symbols as natural numbers which you don't know nor care the actual value of, because as a human you only care about the label you have attached to it, but you do care about its numberness property of it conceptually existing only once.
- pantulis 11mo ago> Dang it, Ruby. You never cease to amaze me! This has been true forever.
- yccs27 11mo agoThe article is a bit confusing, but I like the concept behind symbols: Basically a way to distinguish identifier names (chosen by the programmer) from user-facing strings. The distinction between is one I've mentally adopted in other languages like Python as well. For personal projects I like to use single quotes for 'symbols' and double quotes for "strings", e.g.: add_label(text="Hello World!", align='center') Does anyone else do something similar?
- wild_egg 11mo agoSingle vs double quotes is also semantically significant in Ruby. Double quoted strings support interpolation whereas single quoted does not.
- rubyfan 11mo agoI almost never use single quotes unless I’m in someone else’s code that already used single quotes. And then I have to fight the urge to not change everything to double quotes just because.
- masklinn 11mo ago> Does anyone else do something similar? I do exactly the same thing. Did you also derive it from Erlang?
- Fire-Dragon-DoL 11mo agoI have been with you for a very long time,until I recently realized how tricky the edge cases are, I cannot figure out a good rule. A json api, should it use symbols or strings? Json has string keys, but the api keys are chosen by the programmer. I can't remember which edgecase,but there are another bunch I found that are tricky.
- theoldgreybeard 11mo agoI'm a ruby developer, would never dream of switching to another language as my bread and butter - but my language design hot take for ruby is that symbols were a mistake and unnecessary and the language would have been better off just using frozen (immutable) string literals for everything except for the syntax of keyword arguments. Unfortunately we can't change it now, but generally I just don't use symbols anymore unless I absolutely have to.
- nixpulvis 11mo agoI've always been conflicted on them. I like that they allow me to be more explicit about things which are read from input or parsed out as strings vs internal parts of my program (I rarely call .to_sym), but I've also lost time to being careless and indexing hash maps with the wrong type. Overall, I think I'm glad they exist.
- theoldgreybeard 11mo agoI've had to fix so many errors over the years due to string/symbol mismatch in function calls and/or hash access, and never once have I benefited from the extra optimizations that the guaranteed singular allocation of symbols have given me where I wasn't able to get the same benefit from a frozen string literal. If it was up to me I'd get rid of them, but I know I have an unpopular opinion and it's really only a thought experiment.
- Glyptodon 11mo agoI still wish the Rails enums were written with slightly more object portability.