https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112019-09-07T05:22:06ZRuby Issue Tracking SystemRuby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814362019-09-07T05:22:06Zschneems (Richard Schneeman)
<ul></ul><p>Thank you for writing up the proposal. This would certainly helped my optimization case. Here’s the PR I referenced in my talk where I had to work around the allocations from calling to_s on a symbol: <a href="https://github.com/rails/rails/pull/34197" class="external">https://github.com/rails/rails/pull/34197</a></p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814392019-09-07T08:51:46Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>Another idea: #to_z</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814402019-09-07T09:29:23ZEregon (Benoit Daloze)
<ul></ul><p>Another possibility: make/let #to_s methods of core types return frozen strings.<br>
The general contract of <code>to_s</code> would become: "return a String representing the object, which might be frozen. Use <code>.to_s.dup</code> if you need a mutable string".</p>
<p>That would be less compatible but would have the advantage of existing code already being able to use those savings without changes and complications.</p>
<p>My impression is <code>to_s</code> does already not guarantee to return a new String.<br>
For example, <code>String#to_s</code> returns itself and mutating <code>sth.to_s</code> is therefore not safe if <code>sth</code> can be a String.<br>
Also, <code>to_s</code> definitions for user classes seem likely to already return a frozen String simply because they might use a String literal (interpolated or not) for it and <code>frozen-string-literal: true</code>.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814472019-09-07T11:27:27Zshevegen (Robert A. Heiler)shevegen@gmail.com
<ul></ul><p>Just a short comment - I originally had to write more, but I think my statement would<br>
be too diluted.</p>
<blockquote>
<p>Also, to_s definitions for user classes seem likely to already return<br>
a frozen String simply because they might use a String literal<br>
(interpolated or not) for it and frozen-string-literal: true.</p>
</blockquote>
<p>IMO, it currently can not be assumed to hold true because there may also<br>
be frozen-string-literal: false settings, and frozen strings are not yet<br>
the default nor will be in ruby 3.0, so I would not assume this the case<br>
right now. This may change in the future, but right now I think it would<br>
confuse people if to_s and to_str do NOT return a string or string-like<br>
object; and if a string is returned, it may be frozen, despite frozen-string<br>
literals set to false. The latter would qualify as a bug in my opinion.</p>
<p>I have no specific opinion about the suggested APIs, but the names are a<br>
bit strange; or a bit cumbersome. "fstring" reads a bit like "formatted<br>
string".</p>
<p>Oddly enough, I think #to_z is a bit better simply because it is shorter,<br>
but people may still ask why the "z" is there. I guess to_f was not<br>
offered as suggestion for frozen, due to it already meaning to a float<br>
representation. :)</p>
<p>But in general, I don't really have any pro or con opinion either way. (Most<br>
of the suggestion seems to be more inspired about speed/efficiency and less<br>
about specific end-user use.)</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814492019-09-07T12:05:59ZDan0042 (Daniel DeLorme)
<ul></ul><p>Great idea.</p>
<p>This definitely has to be a new method because using <code>to_s(frozen: true)</code> would require to know if the object supports the <code>frozen</code> keyword for its <code>to_s</code> method.</p>
<p>As a method, the default implementation would be <code>to_s(*a,**o).freeze</code> and interpolations can automatically use that. Then it's just a matter of changing various classes (such as Symbol) so that <code>to_frozen_string</code> is the master implementation and <code>to_s</code> becomes <code>to_frozen_string.dup</code></p>
<p>Maybe it's possible for the parser to lexically convert the expressions <code>-expr.to_s</code> <del>and <code><< expr.to_s</code></del> to use <code>to_frozen_string</code></p>
<p>Naming is hard. Short is good because we want to encourage using that method as much as possible. to_fstr, to_s!, to_fs, to_fzs</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814552019-09-08T02:19:22Zheadius (Charles Nutter)headius@headius.com
<ul></ul><blockquote>
<p>For example, String#to_s returns itself and mutating sth.to_s is therefore not safe if sth can be a String.</p>
</blockquote>
<p>This is a very good point. I'd love to see all immutable core types start returning frozen string but I didn't want to push too hard. It would mean immediate benefits without any changes in existing apps and libraries.</p>
<blockquote>
<p>Naming is hard. Short is good because we want to encourage using that method as much as possible. to_fstr, to_s!, to_fs, to_fzs</p>
</blockquote>
<p>Oh, to_s! is pretty good.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814562019-09-08T02:52:25ZDan0042 (Daniel DeLorme)
<ul></ul><blockquote>
<p>For example, String#to_s returns itself and mutating sth.to_s is therefore not safe if sth can be a String.</p>
</blockquote>
<p><code>String#to_s</code> is of course a special case. But that means if a string is currently not frozen, <code>to_frozen_string</code> would have to do <code>dup.freeze</code>... doesn't that ruin the entire point of this proposal, which is about efficiency? It would mean "a #{str}" creates a duplicate of str because interpolation uses the supposedly efficient <code>to_frozen_string</code>. So maybe this proposal really should be called <code>to_efficient_string</code>, which doesn't guarantee a frozen string but rather attempts to minimize allocations. In that context, <code>to_s!</code> makes even more sense because you don't know what kind of string you'll get.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814592019-09-08T09:28:49ZEregon (Benoit Daloze)
<ul></ul><p>How about specifically making Symbol#to_s return a frozen String?</p>
<p>I tried this trivial patch:</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/string.c b/string.c
index 05ce0ed8d6..1a0fa48a6a 100644
</span><span class="gd">--- a/string.c
</span><span class="gi">+++ b/string.c
</span><span class="p">@@ -10866,7 +10866,9 @@</span> sym_inspect(VALUE sym)
VALUE
rb_sym_to_s(VALUE sym)
{
<span class="gd">- return str_new_shared(rb_cString, rb_sym2str(sym));
</span><span class="gi">+ VALUE str = str_new_shared(rb_cString, rb_sym2str(sym));
+ OBJ_FREEZE(str);
+ return str;
</span> }
</code></pre>
<p>And there are 0 test-spec failures and 0 test-all failures.<br>
So, let's make Symbol#to_s frozen?</p>
<p>I think in general it makes a lot of sense that immutable classes return a frozen String for #to_s.</p>
<p>Making #to_s return a frozen String for mutable core classes like Array or Hash is likely much less interesting, as one cannot cache a single String instance but must check the object's contents everytime (and the #to_s result depends on other objects' #to_s which makes it a lot more complicated).</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814602019-09-08T09:40:35ZEregon (Benoit Daloze)
<ul></ul><p>I also tried this version which always returns the same String for a given Symbol:</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/string.c b/string.c
index 05ce0ed8d6..f306c905bc 100644
</span><span class="gd">--- a/string.c
</span><span class="gi">+++ b/string.c
</span><span class="p">@@ -10866,7 +10866,7 @@</span> sym_inspect(VALUE sym)
VALUE
rb_sym_to_s(VALUE sym)
{
<span class="gd">- return str_new_shared(rb_cString, rb_sym2str(sym));
</span><span class="gi">+ return rb_sym2str(sym);
</span> }
</code></pre>
<p>And that passes both test-spec and test-all, except this test which probably needs to be adapted:</p>
<pre><code>[5/8] Test_StringCapacity#test_capacity_shared = 0.00 s
1) Failure:
Test_StringCapacity#test_capacity_shared [/home/eregon/code/ruby/test/-ext-/string/test_capacity.rb:17]:
<0> expected but was
<26>.
</code></pre>
<p>I'll make a PR.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814612019-09-08T09:58:57ZEregon (Benoit Daloze)
<ul></ul><p>PR at <a href="https://github.com/ruby/ruby/pull/2437" class="external">https://github.com/ruby/ruby/pull/2437</a></p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814622019-09-08T10:10:07Zashmaroli (Ashwin Maroli)
<ul></ul><p>Similar to <code>Symbol#to_s</code>, even <code>nil.to_s</code> allocates a new string.</p>
<pre><code class="shell syntaxhl" data-language="shell">irb<span class="o">(</span>main<span class="o">)</span>:001:0> 5.times <span class="o">{</span> p nil.to_s.__id__ <span class="o">}</span>
21118460
21118340
21118180
21118100
21118000
</code></pre>
<p>IMO, since <code>nil</code> is a singleton, <code>nil.to_s</code> should also return the same empty string on every call.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814632019-09-08T10:52:10Zsawa (Tsuyoshi Sawada)
<ul></ul><p>For methods that convert object to a string, we already have three:</p>
<ul>
<li><code>to_s</code></li>
<li><code>to_str</code></li>
<li><code>inspect</code></li>
</ul>
<p>Adding yet another method (or an option for an existing method) would make the picture too complicated. It might be a good time to reconsider the division of labor between the existing three methods.</p>
<p>It is suspicious whether the distinction between <code>to_s</code> vs. <code>to_str</code>, <code>to_h</code> vs. <code>to_hash</code>, <code>to_a</code> vs. <code>to_ary</code>, or <code>to_i</code> vs. <code>to_int</code> etc. is nowadays used in the way it was intended at the beginning. Note that, while the single splat <code>*</code> on an object implicitly calls the explicit <code>to_a</code>, the double splat <code>**</code> implicitly calls the implicit <code>to_hash</code>. Also note that there is no explicit counterpart for the implicit method <code>to_proc</code>, which is used as in <code>Symbol#to_proc</code>, <code>Method#to_proc</code>, <code>Hash#to_proc</code>, where the (missing) explicit counterpart should be used.</p>
<p>Since the implicit vs. explicit distinction is messed up, but we are not having a big problem with it, there is not much motivation to try to maintain such distinction.</p>
<p>Personally, I am not that sure why we need <code>to_str</code> just to show that an object is string-like. For example, we could have the argument of <code>Array#join</code> undergo <code>to_s</code> (with a fallback to <code>to_str</code>) instead of (just) <code>to_str</code>. I don't see any problem with that.</p>
<p>As an example of rearranging the division of labour, one method out of the three mentioned above could be used for creating a new string instance, and another method for referencing a cached representation.</p>
<p>But that is just an example. There may be other ways that can make more sense. The point is, we can perhaps make use of the existing methods rather than adding a new one.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814702019-09-08T20:16:18ZEregon (Benoit Daloze)
<ul></ul><p>ashmaroli (Ashwin Maroli) wrote:</p>
<blockquote>
<p>Similar to <code>Symbol#to_s</code>, even <code>nil.to_s</code> allocates a new string.</p>
</blockquote>
<p>Right, as mentioned in the description, nil, true and false#to_s could all be frozen and cached the same way as for Symbol#to_s.<br>
Although I'd think calling #to_s on those is already less frequent and performance-sensitive than Symbol#to_s.</p>
<p>Are there other immutable types where it would be worthwhile to cache the #to_s result?<br>
I can't find any now, at least it doesn't seem worthwhile for numeric types, and most other core types are not immutable.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814742019-09-09T02:36:14ZDan0042 (Daniel DeLorme)
<ul></ul><blockquote>
<p>So, let's make Symbol#to_s frozen?<br>
I think in general it makes a lot of sense that immutable classes return a frozen String for #to_s.</p>
</blockquote>
<p>That makes sense, and I agree that's the cleanest solution. There's really nothing in the contract that says <code>to_s</code> is supposed to return a non-frozen string.<br>
But I can't agree to a backward-incompatible change without a proper deprecation period.<br>
So I went and added Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: eventually_frozen flag to gradually phase-in frozen strings (Closed)" href="https://bugs.ruby-lang.org/issues/16153">#16153</a> for a way to allow a phase-in period for frozen <code>Symbol#to_s</code>.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=814992019-09-10T07:25:21ZEregon (Benoit Daloze)
<ul></ul><p>Dan0042 (Daniel DeLorme) wrote:</p>
<blockquote>
<p>But I can't agree to a backward-incompatible change without a proper deprecation period.<br>
So I went and added Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: eventually_frozen flag to gradually phase-in frozen strings (Closed)" href="https://bugs.ruby-lang.org/issues/16153">#16153</a> for a way to allow a phase-in period for frozen <code>Symbol#to_s</code>.</p>
</blockquote>
<p>I'm unsure if such troubles are necessary, because the practical incompatibility might be extremely low, such as illustrated by in-repository specs and tests.</p>
<p>Do we have an example of real code breaking because of Symbol#to_s returning a frozen String?<br>
Could someone try my PR on their app and see if it breaks anything?</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=815002019-09-10T08:12:28ZEregon (Benoit Daloze)
<ul></ul><p>I tried running ActiveSupport tests from Rails master on Ruby 2.6.2 + my PR, and found that one change is needed:</p>
<pre><code>Error:
ConfigurableActiveSupport#test_configuration_accessors_are_not_available_on_instance:
FrozenError: can't modify frozen String
/home/eregon/code/rails/activesupport/lib/active_support/ordered_options.rb:43:in `chomp!'
/home/eregon/code/rails/activesupport/lib/active_support/ordered_options.rb:43:in `method_missing'
...
</code></pre>
<p><a href="https://github.com/rails/rails/blob/7af44f49dbf221c3b7f0b0d476913a74b6a1d0e4/activesupport/lib/active_support/ordered_options.rb#L42-L43" class="external">https://github.com/rails/rails/blob/7af44f49dbf221c3b7f0b0d476913a74b6a1d0e4/activesupport/lib/active_support/ordered_options.rb#L42-L43</a><br>
A simple fix is to use <code>name_string = +name.to_s</code>, or to refactor to use <code>start_with?</code> + <code>name_string[0..-2]</code> instead of <code>chomp!</code>.<br>
Then all ActiveSupport tests pass.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=815082019-09-10T19:06:57Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/772">@Eregon (Benoit Daloze)</a>, for context Matz recently refused a very similar change <a href="https://bugs.ruby-lang.org/issues/15836" class="external">https://bugs.ruby-lang.org/issues/15836</a></p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=815922019-09-19T07:47:10Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>For frozen <code>Symbol#to_s</code>, I see a clear benefit. But I worry a little bit for incompatibility.<br>
So how about making an experiment by the next preview(2) to see how big the incompatibility is?<br>
<code>String#to_s</code> etc. is a different story. we need to discuss further.</p>
<p>Matz.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817212019-09-25T17:30:39Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> Great to hear.</p>
<p>I get you are worried about <code>String#to_s</code>, but what about others that have been mentioned here, namely:</p>
<ul>
<li><code>NilClass#to_s</code></li>
<li><code>TrueClass/FalseClass#to_s</code></li>
<li><code>Module#name</code></li>
</ul> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817262019-09-25T20:41:51ZEregon (Benoit Daloze)
<ul></ul><blockquote>
<p>So how about making an experiment by the next preview(2) to see how big the incompatibility is?</p>
</blockquote>
<p>Thank you, I will merge <a href="https://github.com/ruby/ruby/pull/2437" class="external">https://github.com/ruby/ruby/pull/2437</a> and mark the change as experimental.</p>
<blockquote>
<p>String#to_s etc. is a different story.</p>
</blockquote>
<p>I agree String#to_s should remain as-is.<br>
Returning a frozen String for String#to_s would just add more allocations, since the current String#to_s just returns <code>self</code>.</p>
<blockquote>
<p>what about others that have been mentioned here</p>
</blockquote>
<p>I think nil/true/false.to_s and Module#name could be frozen, much like Symbol#to_s.<br>
There might be some incompatible usages but I would expect very rare, and the FrozenError would make it pretty clear how to deal with it<br>
(just add String#+@ or #dup, we could even adapt the FrozenError message for Strings to mention this if desired).</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817272019-09-26T00:42:38Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>byroot (Jean Boussier) wrote:</p>
<blockquote>
<p>I get you are worried about <code>String#to_s</code>, but what about others that have been mentioned here, namely:</p>
<ul>
<li><code>NilClass#to_s</code></li>
<li><code>TrueClass/FalseClass#to_s</code></li>
<li><code>Module#name</code></li>
</ul>
</blockquote>
<p>Ok for the experiment.</p>
<p>Matz.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817332019-09-26T08:23:29ZEregon (Benoit Daloze)
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li></ul><p>Applied in changeset <a class="changeset" title="[EXPERIMENTAL] Make Symbol#to_s return a frozen String * Always the same frozen String for a giv..." href="https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/6ffc045a817fbdf04a6945d3c260b55b0fa1fd1e">git|6ffc045a817fbdf04a6945d3c260b55b0fa1fd1e</a>.</p>
<hr>
<p>[EXPERIMENTAL] Make Symbol#to_s return a frozen String</p>
<ul>
<li>Always the same frozen String for a given Symbol.</li>
<li>Avoids extra allocations whenever calling Symbol#to_s.</li>
<li>See [Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Add a way to request a frozen string from to_s (Open)" href="https://bugs.ruby-lang.org/issues/16150">#16150</a>]</li>
</ul> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817342019-09-26T08:33:30ZEregon (Benoit Daloze)
<ul><li><strong>Status</strong> changed from <i>Closed</i> to <i>Assigned</i></li><li><strong>Assignee</strong> set to <i>Eregon (Benoit Daloze)</i></li></ul><p>Reopening, the issue was accidentally closed as I merged the Symbol#to_s change.<br>
I will make a PR for nil/true/false.to_s and Module#name to return a frozen String.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817352019-09-26T11:00:01Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>I got the <code>Module#name</code> one ready here: <a href="https://github.com/ruby/ruby/pull/2487" class="external">https://github.com/ruby/ruby/pull/2487</a></p>
<p>For <code>nil/true/false</code> it is a bit trickier because the returned string are ASCII encoded, so I'm not quite sure how to statically create ASCII <code>fstrings</code> with the existing APIs.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817362019-09-26T11:25:50Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Closed</i></li></ul><p>Applied in changeset <a class="changeset" title="[EXPERIMENTAL] Make Module#name return a frozen String * Always the same frozen String for a..." href="https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/9d0866c7d7b9cbe36a851744a37806e747e0e7a8">git|9d0866c7d7b9cbe36a851744a37806e747e0e7a8</a>.</p>
<hr>
<p>[EXPERIMENTAL] Make Module#name return a frozen String</p>
<pre><code>* Always the same frozen String for a given Module or Class.
* Avoids extra allocations whenever calling Module#name.
* See [Feature #16150]
</code></pre> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817372019-09-26T11:26:55ZEregon (Benoit Daloze)
<ul><li><strong>Status</strong> changed from <i>Closed</i> to <i>Assigned</i></li></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/7941">@byroot (Jean Boussier)</a> Great, I merged your PR.<br>
Sorry, I should have asked first if you wanted to do it.</p>
<p>Do you want to do the PR for <code>nil</code>/<code>true</code>/<code>false</code> as well then?</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817382019-09-26T11:50:42Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>Do you want to do the PR for nil/true/false as well then?</p>
</blockquote>
<p>I tried but I'm quite unsure how to do it, so I don't mind if you handle it.</p>
<p>On a side note I'm currently running our test suite with the following monkey patch:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">FreezeName</span>
<span class="k">def</span> <span class="nf">name</span>
<span class="nb">name</span> <span class="o">=</span> <span class="k">super</span>
<span class="nb">name</span> <span class="o">=</span> <span class="o">-</span><span class="nb">name</span> <span class="k">if</span> <span class="nb">name</span>
<span class="nb">name</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Module</span><span class="p">.</span><span class="nf">singleton_class</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">FreezeName</span><span class="p">)</span>
<span class="k">module</span> <span class="nn">FreezeToS</span>
<span class="k">def</span> <span class="nf">to_s</span>
<span class="o">-</span><span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Symbol</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">FreezeToS</span><span class="p">)</span>
<span class="no">NilClass</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">FreezeToS</span><span class="p">)</span>
<span class="no">TrueClass</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">FreezeToS</span><span class="p">)</span>
<span class="no">FalseClass</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">FreezeToS</span><span class="p">)</span>
</code></pre>
<p>This way I should be able to identify some breakage across a large amount of gems.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817412019-09-26T13:23:18Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>byroot (Jean Boussier) wrote:</p>
<blockquote>
<p>For <code>nil/true/false</code> it is a bit trickier because the returned string are ASCII encoded, so I'm not quite sure how to statically create ASCII <code>fstrings</code> with the existing APIs.</p>
</blockquote>
<pre><code class="C syntaxhl" data-language="C"><span class="k">static</span> <span class="n">VALUE</span>
<span class="nf">true_to_s</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">obj</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">rb_fstring_enc_lit</span><span class="p">(</span><span class="s">"true"</span><span class="p">,</span> <span class="n">rb_usascii_encoding</span><span class="p">());</span>
<span class="p">}</span>
</code></pre> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817422019-09-26T13:35:16Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>Ok, so I just finished updating our application to be compatible with both <code>Symbol#to_s</code> and <code>Module#name</code> returning frozen strings.</p>
<p>Over the 502 gems used in the repo, I found 5 impacted gems, all because of <code>Symbol#to_s</code>, and all in very minor ways:</p>
<ul>
<li>A <a href="https://github.com/pry/pry/blob/fa97d5c2997ff1cf03cf925df482a7a1d9ca3ea3/lib/pry/slop.rb#L457" class="external"><code>method_missing</code> in Pry</a>
</li>
<li>Some <a href="https://github.com/grpc/grpc/blob/9810d217709e536a49625c32d45ff6cbbe535e18/src/ruby/lib/grpc/generic/service.rb#L33-L39" class="external">string manipulation in the grpc gem</a>
</li>
<li>Also some encoding coercion in <a href="https://github.com/wvanbergen/activerecord-databasevalidations/pull/23" class="external">activerecord-databasevalidations</a>
</li>
<li>The <a href="https://github.com/matthewrudy/memoist/blob/510cb571cdea0fcb72fa8ec7560bc876f933f442/lib/memoist.rb#L40-L51" class="external">memoist gem</a>
</li>
<li><a href="https://github.com/googleapis/google-cloud-ruby/blob/bbaf9f27e8ea8251a99049cf99fed9c755a29dd1/google-cloud-core/lib/google/cloud/config.rb#L458-L466" class="external">Another <code>method_missing</code> in google-cloud-core</a></li>
<li>And of course the Rails one mentioned above but it got fixed this morning.</li>
</ul>
<p>Inside our repo of over 1 million lines of code, I found 4 impacted methods, all were trivially fixed. Again they were all caused by <code>Symbol#to_s</code>, and very similar in nature to the public ones I listed above.</p>
<p>Overall I was able to trivially update a gigantic app to be ready for this change, all that in under an hour.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817432019-09-26T13:37:32Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/4">@nobu (Nobuyoshi Nakada)</a> Yes that's what I had in mind, but then from what I understand it means we have to lookup the fstring table every time rather than just return a existing pointer. So it's not really an optimisation, is it?</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817442019-09-26T14:32:37Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>byroot (Jean Boussier) wrote:</p>
<blockquote>
<p>Over the 502 gems used in the repo, I found 5 impacted gems, all because of <code>Symbol#to_s</code>, and all in very minor ways:</p>
</blockquote>
<p>A great report. For the record, I add another case I've heard:</p>
<ul>
<li>did_you_mean: <a href="https://github.com/yuki24/did_you_mean/pull/125/commits/ab41208165828b1df31b603e9967ea48b7fad022" class="external">https://github.com/yuki24/did_you_mean/pull/125/commits/ab41208165828b1df31b603e9967ea48b7fad022</a>
</li>
</ul>
<p>Currently, seven impacted cases are found.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817462019-09-26T14:42:58Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>Then store those objects in global variables, and <code>rb_gc_register_mark_object</code>.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817532019-09-26T21:39:04Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>I'm glad to see some smaller parts of this are moving forward. A few updates from my end (JRuby):</p>
<ul>
<li>Frozen strings from Symbol etc:</li>
</ul>
<p>We are on board with making Symbol, nil, true, false, and Module all return frozen strings, and have an open PR to do this for Symbol right now: <a href="https://github.com/jruby/jruby/pull/5868" class="external">https://github.com/jruby/jruby/pull/5868</a></p>
<ul>
<li>String#to_s concerns:</li>
</ul>
<p>Right now since it returns self, it may be frozen or unfrozen, so you are NEVER safe mutating the result of to_s directly. With the changes above, even more types will start returning frozen strings. I feel like we need a way to audit the mutation of to_s results we want to make frozen in the future, similar to the --debug:frozen-string-literal feature that prints out where a frozen string came from if you try to mutate it.</p>
<p>I guess my logic boils down to this right now:</p>
<ul>
<li>to_s often returns a frozen string right now.</li>
<li>to_s will return more frozen strings in the future if these PRs make it into 2.7.</li>
<li>Both now and in the future, it is not safe to mutate the results of a to_s.</li>
<li>So, let's give people a way to find these mutations as part of 2.7, so we can help eliminate such cases.</li>
</ul> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817552019-09-26T23:14:18Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/286">@headius (Charles Nutter)</a> I totally get your opinion on <code>String#to_s</code>, however on a very practical standpoint I agree with <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/772">@Eregon (Benoit Daloze)</a>:</p>
<blockquote>
<p>Returning a frozen String for String#to_s would just add more allocations, since the current String#to_s just returns self.</p>
</blockquote>
<p>If we were to freeze the return of <code>String#to_s</code> I wouldn't be surprised if we ended up causing much more allocation that we're saving with <code>Symbol#to_s</code> and <code>Module#name</code>.</p>
<p>But granted that this now cause this somewhat weird situation where <code>to_s</code> might or might not return a frozen string. that being said it's already kinda the case:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="o">>></span> <span class="s2">"foo"</span><span class="p">.</span><span class="nf">freeze</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">frozen?</span>
<span class="o">=></span> <span class="kp">true</span>
</code></pre> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817562019-09-26T23:49:02Zheadius (Charles Nutter)headius@headius.com
<ul></ul><blockquote>
<p>But granted that this now cause this somewhat weird situation where to_s might or might not return a frozen string. that being said it's already kinda the case</p>
</blockquote>
<p>Yeah, this is precisely the problem. It is made even worse by the large number of libraries that already set <code>frozen-string-literal</code>.</p>
<p>One thing we should all be able to agree on at this point: it is not safe to blindly mutate the result of <code>String#to_s</code>, since more and more cases will produce frozen strings. With the experimental changes accepted above, an additional set of <code>to_s</code> results will also be frozen. So, by extension, it is not now and will never be safe to blindly mutate the result of calling any <code>to_s</code>.</p>
<p>I'd argue it's actually NEVER safe to modify the result of calling <code>to_s</code>, because for mutable source strings you'd be mutating the original contents! This is probably not desired behavior in the large majority of existing code. With that condition in mind, it would actually be much <em>safer</em> for us to start returning frozen strings from String#to_s as soon as possible.</p>
<p>I know this is a difficult pill to swallow. The original purpose of this issue was to consider adding a way to <em>request</em> frozen strings from to_s, since that would at least be an opt-in. Hence the suggestions of a to_z or similar new mechanism.</p>
<p>In the 2.7 timeframe, I'd like to see some combination of the following:</p>
<ul>
<li>A way to explicitly request a frozen string from any object, such as <code>to_z</code>, so code could start migrating toward frozen strings today.</li>
</ul>
<p>This would be an extension of existing <code>"str".freeze</code> and <code>-"str"</code> logic for explicitly requesting a single frozen string literal, but would allow making this request for any <code>to_s</code> result.</p>
<ul>
<li>A debug mode that would warn if code attempts to mutate the result of <code>String#to_s</code> (e.g. <code>--debug:frozen-string-to_s</code>), since based on the above conditions this is almost never advisable.</li>
</ul>
<p>This will help us audit existing code and start "fixing" it right now without a hard break. I'd like to see the above warning all the time, but that would require tracking source file and line for all literal strings (as in the current <code>--debug:frozen-string-literal</code>).</p>
<ul>
<li>A pragma to explicitly set a file as having mutable-string-literal, as a local escape hatch for future default frozen-string-literal.</li>
<li>A command-line flag to force mutable-string-literal when no pragma exists, as a global escape hatch for future default frozen-string-literal.</li>
</ul>
<p>These are a simple way to guarantee a soft landing if (when?) we default to <code>frozen-string-literal</code> globally in 3.0. All cases that would break with default <code>frozen-string-literal</code> could at least be made to work with the flag.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817572019-09-26T23:53:52Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>Oh I should point out another problem with the current situation...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">some_str</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">freeze</span>
</code></pre>
<p>The above code is also unsafe right now, because since <code>to_s</code> returns self, we're now freezing the original string!</p>
<p>In order to safely get a frozen string from an object, you must ALWAYS do <code>some_str.to_s.dup.freeze</code>. In order to safely get a mutable string from an object, you must ALWAYS do <code>some_str.to_s.dup</code>.</p>
<p>So what are we saving by leaving the current unsafe behavior in place?</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817592019-09-27T04:53:01Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Closed</i></li></ul><p>Applied in changeset <a class="changeset" title="[EXPERIMENTAL] Make NilClass#to_s, TrueClass#to_s and FalseClass#to_s return a frozen String ..." href="https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/eff15a269fdc37d2b09cf1dfe8c1b1bf6e377a32">git|eff15a269fdc37d2b09cf1dfe8c1b1bf6e377a32</a>.</p>
<hr>
<p>[EXPERIMENTAL] Make NilClass#to_s, TrueClass#to_s and FalseClass#to_s return a frozen String</p>
<pre><code>* Always the same frozen String for each of these values.
* Avoids extra allocations whenever calling these 3 methods.
* See [Feature #16150]
</code></pre> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817612019-09-27T08:12:32Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>In order to safely get a frozen string from an object, you must ALWAYS do <code>some_str.to_s.dup.freeze</code>. In order to safely get a mutable string from an object, you must ALWAYS do <code>some_str.to_s.dup</code>.</p>
</blockquote>
<p>Not exactly, <code>-@</code> and <code>+@</code> makes this much simpler.</p>
<p><code>-@</code> will return self if the string is already frozen.</p>
<p><code>+@</code> will return self if the string is already mutable.</p>
<p>So these two allows you to safely get either a frozen or mutable string with the minimum amount of allocations.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817812019-09-28T04:33:32Zheadius (Charles Nutter)headius@headius.com
<ul></ul><blockquote>
<p>Not exactly, -@ and +@ makes this much simpler</p>
</blockquote>
<p>I do like the unary operators, but they also have some precedence oddities:</p>
<pre><code>>> -"foo".size
=> -3
>> (-"foo").size
=> 3
</code></pre>
<p>And it doesn't work at all if you're chaining method calls:</p>
<pre><code>>> +ary.to_s.frozen?
NoMethodError: undefined method `+@' for false:FalseClass
from (irb):8
from /usr/bin/irb:11:in `<main>'
</code></pre>
<p>But you are right, instead of the explicit <code>dup</code> with possible freeze you could use <code>-</code> or <code>+</code> on the result of <code>to_s</code>. However it's still not safe to modify it since it would modify the original string too.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817822019-09-28T04:43:12Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>Hi,</p>
<p>In message "Re: <a href="/issues/16150">[ruby-core:95142]</a> [Ruby master Feature#16150] Add a way to request a frozen string from to_s"<br>
on Sat, 28 Sep 2019 04:33:32 +0000 (UTC), <a href="mailto:headius@headius.com" class="email">headius@headius.com</a> writes:</p>
<blockquote>
<p>Issue <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Add a way to request a frozen string from to_s (Open)" href="https://bugs.ruby-lang.org/issues/16150">#16150</a> has been updated by headius (Charles Nutter).</p>
<blockquote>
<p>Not exactly, -@ and +@ makes this much simpler</p>
</blockquote>
<p>I do like the unary operators, but they also have some precedence oddities:</p>
<pre><code>>> -"foo".size
=> -3
>> (-"foo").size
=> 3
</code></pre>
<p>And it doesn't work at all if you're chaining method calls:</p>
</blockquote>
<p>How about making String#+ and #- without argument behave like #+@ and #-@ respectively, so that we can write:</p>
<pre><code>"foo".-.size
ary.+.to_s.frozen?
</code></pre>
<pre><code> matz.
</code></pre> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817842019-09-29T12:29:25Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>However it's still not safe to modify it since it would modify the original string too.</p>
</blockquote>
<p>IMHO that's two different use cases. Either:</p>
<ul>
<li>You want to be sure not to mutate the string, then you use <code>.dup</code>
</li>
<li>You don't care about mutating the original string, you just want to minimize allocations, then you use <code>+@</code>.</li>
</ul>
<p>It's similar with <code>freeze</code> and <code>-@</code>. Either:</p>
<ul>
<li>You simply want to prevent the string from being mutated, you use <code>.freeze</code>
</li>
<li>You know you're going to hold on that string and want to optimize memory retention, you use <code>-@</code>.</li>
</ul>
<blockquote>
<p>How about making String#+ and #- without argument behave like #+@ and #-@ respectively, so that we can write:</p>
</blockquote>
<p>IMHO <code>.-</code> and <code>.+</code> is not very elegant. Proper method names explaining the intent would be preferable.</p>
<ul>
<li>
<code>-@</code> could be <code>dedup</code>, or <code>deduplicate</code>.</li>
<li>
<code>+@</code> could be <code>mutable</code> or <code>mut</code>.</li>
</ul> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817912019-09-29T19:29:08Zjonathanhefner (Jonathan Hefner)jonathan@hefner.pro
<ul></ul><blockquote>
<ul>
<li>
<code>-@</code> could be <code>dedup</code>, or <code>deduplicate</code>.</li>
</ul>
</blockquote>
<p><code>dedup</code> is an interesting choice because it implies the opposite of <code>dup</code>.</p>
<p>Although you might run into naming collisions if / when you extend this functionality to other types. For example, I would expect <code>Array#dedup</code> to have a very different meaning, entirely unrelated to freezing.</p>
<p>Another alternative might be something like <code>intern_s</code>, since <code>String#intern</code> already exists. (Although this might also be awkward to extend to other types. For example, should it be <code>Array#intern_a</code> for consistency or simply <code>Array#intern</code>?)</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=817932019-09-29T22:08:14ZEregon (Benoit Daloze)
<ul></ul><p>I'll get to the point because I think we're starting to discuss unrelated things in this issue.</p>
<p>headius (Charles Nutter) wrote:</p>
<blockquote>
<p>I'd argue it's actually NEVER safe to modify the result of calling <code>to_s</code> [...]</p>
</blockquote>
<p>Yes, and it has never been.<br>
<code>String#to_s</code> has always returned itself, including for frozen strings, so of course <code>to_s</code> means "convert to a String if you're not already" and gives no guarantee to return a new String ready to be mutated.<br>
It is the same for all other <code>to_*</code> methods, isn't it?</p>
<p>I think this is already well understood by Rubyists, and the changes for Symbol#to_s, Module#name, true/false/nil.to_s just make an existing behavior a bit more common.<br>
So, I think there is nothing to change about <code>String#to_s</code>, except maybe improving the documentation of <code>Kernel#to_s</code> in general.</p>
<blockquote>
<p>I know this is a difficult pill to swallow. The original purpose of this issue was to consider adding a way to <em>request</em> frozen strings from to_s, since that would at least be an opt-in. Hence the suggestions of a to_z or similar new mechanism.</p>
</blockquote>
<p>I think there is no need for yet another "convert to String" method.<br>
We changed the methods that used to allocate on every call instead of returning an internal frozen String (I don't think there are others, are there?).</p>
<p>Making String#to_s not return <code>self</code> is just worse for allocations, and would be inconsistent with other <code>to_*</code> methods.<br>
Is there any advantage to make String#to_s not return <code>self</code>?</p>
<blockquote>
<ul>
<li>A way to explicitly request a frozen string from any object, such as <code>to_z</code>, so code could start migrating toward frozen strings today.</li>
</ul>
</blockquote>
<p>Why are <code>String#-@</code> and <code>str.dup.freeze</code> not enough?</p>
<blockquote>
<ul>
<li>A pragma to explicitly set a file as having mutable-string-literal, as a local escape hatch for future default frozen-string-literal.</li>
<li>A command-line flag to force mutable-string-literal when no pragma exists, as a global escape hatch for future default frozen-string-literal.</li>
</ul>
</blockquote>
<p>That's about frozen string literals, <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Immutable String literal in Ruby 3 (Closed)" href="https://bugs.ruby-lang.org/issues/11473">#11473</a>, please comment there, I don't think we should mix both issues.</p>
<p>In fact, I think this issue is done and is already correctly closed.<br>
We addressed the cases that were allocating too much, without the need for a new method for converting to strings.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=822072019-10-21T13:09:22Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>Quick update on compatibility with this change.</p>
<p>I opened PRs on the 5 affected gems I found:</p>
<ul>
<li><a href="https://github.com/googleapis/google-cloud-ruby/pull/4109" class="external">https://github.com/googleapis/google-cloud-ruby/pull/4109</a></li>
<li><a href="https://github.com/matthewrudy/memoist/pull/82" class="external">https://github.com/matthewrudy/memoist/pull/82</a></li>
<li><a href="https://github.com/grpc/grpc/pull/20417" class="external">https://github.com/grpc/grpc/pull/20417</a></li>
<li><a href="https://github.com/pry/pry/pull/2084" class="external">https://github.com/pry/pry/pull/2084</a></li>
<li><a href="https://github.com/wvanbergen/activerecord-databasevalidations/pull/23" class="external">https://github.com/wvanbergen/activerecord-databasevalidations/pull/23</a></li>
</ul>
<p>So far 2 have been merged and released, and 3 are awaiting a reaction from their respective maintainers.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824112019-11-01T00:55:37Zhsbt (Hiroshi SHIBATA)hsbt@ruby-lang.org
<ul></ul><p>This change must be reverted.</p>
<p>I got the issue report from Heroku users.</p>
<p><a href="https://github.com/heroku/heroku-buildpack-ruby/issues/833#issuecomment-548592514" class="external">https://github.com/heroku/heroku-buildpack-ruby/issues/833#issuecomment-548592514</a></p>
<p>When Rails 6.0.0 and old version users specified Ruby 2.7, Their application is not working with Ruby 2.7.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824202019-11-01T08:54:10Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>OK, frozen <code>Symbol#to_s</code> should be reverted. Maybe we can challenge in the future (with the deprecation process first).</p>
<p>Matz.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824262019-11-01T17:43:22ZEregon (Benoit Daloze)
<ul></ul><p>hsbt (Hiroshi SHIBATA) wrote:</p>
<blockquote>
<p>This change must be reverted.</p>
</blockquote>
<p>I don't think it's fair to decide that on your own, from apparently just one issue.</p>
<p>Could you detail the situation, i.e., in which place the problem occurs and why can't we make a release of that gem before 2.7.0 is out?<br>
The user seems to say it's not caused by Rails.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824272019-11-01T17:56:04ZEregon (Benoit Daloze)
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/5459">@rafaelfranca (Rafael França)</a> told me Rails 6.0.1 is scheduled for November 5, way before the Ruby 2.7 release (see <a href="https://weblog.rubyonrails.org/2019/10/31/Rails-6-0-1-rc1-released/" class="external">https://weblog.rubyonrails.org/2019/10/31/Rails-6-0-1-rc1-released/</a>).<br>
So if that issue is caused by Rails, it should be fixed before any stable release of Ruby 2.7 comes out.</p>
<p>"Deprecation" for <code>String#to_sym</code> to return a frozen String seems unfortunately very complicated.<br>
<a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: eventually_frozen flag to gradually phase-in frozen strings (Closed)" href="https://bugs.ruby-lang.org/issues/16153">#16153</a> exposes one way to do it but that didn't get much agreement, "half-frozen Strings" don't make much sense to me and I would think are not intuitive to many.</p>
<p>So, given that this change has AFAIK a very limited impact and well-evaluated by <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/7941">@byroot (Jean Boussier)</a>, I think it is fine to keep the change.</p>
<p><a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Regexp#{match,match?} with a nil argument are deprecated and will raise a TypeError in Ruby 3.0 (Closed)" href="https://bugs.ruby-lang.org/issues/13083">#13083</a> OTOH is a far more breaking change and that one can easily go through deprecation first.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824342019-11-01T23:16:26Zhsbt (Hiroshi SHIBATA)hsbt@ruby-lang.org
<ul></ul><blockquote>
<p>So if that issue is caused by Rails, it should be fixed before any stable release of Ruby 2.7 comes out.</p>
</blockquote>
<p>This change affects many developers, company and applications with upgrading work. How about Rails 5.2 and 5.1 users? Do we abandon them?</p>
<p>You should think about the cost of upgrading the real-world application. The upgrading Rails is different from the one library.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824352019-11-01T23:37:59Zrafaelfranca (Rafael França)rafael@franca.dev
<ul></ul><p>This issue is also fixed on Rails 5.2 and will be released in the next version of Rails.</p>
<p>Users of any version of Rails below 5.2 are already abandoned. The Rails core team don't support those versions anymore. So, they already need to upgrade their applications, with this change or not.</p>
<p>We could still say that users would still need to upgrade to Rails 6.0.1 because of this change but in my opinion that is already required. Users that stay on Rails 6.0.0 would not receive security upgrades without having to upgrade to 6.0.1 (or the latest version before the security patch) first.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824422019-11-02T13:14:32ZEregon (Benoit Daloze)
<ul></ul><p>The plan is Rails 6 users can update to Rails 6.0.1 and Rails 5.2 users can update to Rails 5.2.4 (assuming 5.2.4 will be released before 2.7, we need to confirm with Rails devs).<br>
That should be easy and recommended for security and other bug fixes anyway.</p>
<p>I agree it's not ideal, but I also feel it's really not the biggest problem for Ruby 2.7 in production, and we already have a good plan.<br>
The fact that people trying Ruby 2.7 will get many keyword argument warnings (which also affects performance) seems far more problematic to me (<a href="https://bugs.ruby-lang.org/issues/16289#note-5" class="external">https://bugs.ruby-lang.org/issues/16289#note-5</a>).<br>
And keyword arguments warnings fixes are currently not backported (I guess because they require so many changes).</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824782019-11-05T08:38:20Znaruse (Yui NARUSE)naruse@airemix.jp
<ul><li><strong>Status</strong> changed from <i>Closed</i> to <i>Open</i></li></ul><p>Eregon (Benoit Daloze) wrote:</p>
<blockquote>
<p>I agree it's not ideal, but I also feel it's really not the biggest problem for Ruby 2.7 in production, and we already have a good plan.<br>
The fact that people trying Ruby 2.7 will get many keyword argument warnings (which also affects performance) seems far more problematic to me (<a href="https://bugs.ruby-lang.org/issues/16289#note-5" class="external">https://bugs.ruby-lang.org/issues/16289#note-5</a>).<br>
And keyword arguments warnings fixes are currently not backported (I guess because they require so many changes).</p>
</blockquote>
<p>Yes, keyword argument is the most difficult challenge for Ruby 2.7.<br>
And it is also very important toward Ruby 3.0.<br>
As a release manager I don't want to add more trouble like Symbol#to_s.</p>
<p>As already Matz stated, I reverted this at bea322a3.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824812019-11-05T11:00:40Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>If that change is reverted, could we make it a deprecation instead?</p>
<p>Any idea how such deprecation warning would be implemented?</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824942019-11-05T21:30:38ZEregon (Benoit Daloze)
<ul></ul><p>naruse (Yui NARUSE) wrote:</p>
<blockquote>
<p>As already Matz stated, I reverted this at bea322a3.</p>
</blockquote>
<p>So you reverted without a single case that was actually problematic due to the change?<br>
I'm very disappointed, deprecation without a proper explanation seems inappropriate.</p>
<p>I expect a clear explanation of why this is problematic.<br>
It is not problematic for the case of Rails, as clearly explained in the comments above.</p>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> On what did you base your decision? I hope something else than a single unclear bug report.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824952019-11-05T22:24:34ZEregon (Benoit Daloze)
<ul></ul><p>I'm sorry if the above is rude.</p>
<p>I find it frustrating that the change is reverted without much else than "a possible incompatibility reported by one user" and for which we already have a plan to address it easily (just use the latest Rails of that release series, which is likely already needed for other fixes).</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=824992019-11-06T00:29:03Zduerst (Martin Dürst)duerst@it.aoyama.ac.jp
<ul></ul><p>Just to cross-check:</p>
<p>Eregon (Benoit Daloze) wrote:</p>
<blockquote>
<p>I find it frustrating that the change is reverted without much else than "a possible incompatibility reported by one user"</p>
</blockquote>
<p>I didn't find the text "a possible incompatibility reported by one user" anywhere in this issue. Hiroshi (Shibata) writes "I got the issue report from Heroku users.", which indicates it's more than a single user.</p>
<blockquote>
<p>and for which we already have a plan to address it easily (just use the latest Rails of that release series, which is likely already needed for other fixes).</p>
</blockquote>
<p>I think it's not a bad idea to have a bit of slack for version combinations. If we put too many restrictions on what version of something can be combined with what version of something else, we easily get into situations where there are no viable combinations anymore.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=825002019-11-06T00:34:45Zhsbt (Hiroshi SHIBATA)hsbt@ruby-lang.org
<ul></ul><p>This discussion started with "how big the incompatibility is?" at <a href="https://bugs.ruby-lang.org/issues/16150#note-18" class="external">https://bugs.ruby-lang.org/issues/16150#note-18</a></p>
<p>The old version of Rails is incompatible. And I also got the issue of <code>middleman</code> depends on <code>memoist</code>. So, my company blog built by <code>middleman</code> is broken now with Ruby 2.7.0-preview2.</p>
<p>We found the big incompatibility now. We should restart to discuss this feature.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=825072019-11-06T10:27:13ZEregon (Benoit Daloze)
<ul></ul><p>duerst (Martin Dürst) wrote:</p>
<blockquote>
<p>I didn't find the text "a possible incompatibility reported by one user" anywhere in this issue.</p>
</blockquote>
<p>It's my interpretation and the only piece of information I had so far from <a href="https://github.com/heroku/heroku-buildpack-ruby/issues/833#issuecomment-548592514" class="external">https://github.com/heroku/heroku-buildpack-ruby/issues/833#issuecomment-548592514</a><br>
That bug report is rather unclear, apparently even using latest Rails didn't solve the issue, and previous comments on that issue are completely unrelated.</p>
<blockquote>
<p>I think it's not a bad idea to have a bit of slack for version combinations. If we put too many restrictions on what version of something can be combined with what version of something else, we easily get into situations where there are no viable combinations anymore.</p>
</blockquote>
<p>I would think it's always advisable to update Rails before Ruby.<br>
The other way around seems a recipe for more warnings and more issues.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=825082019-11-06T10:37:44ZEregon (Benoit Daloze)
<ul></ul><p>hsbt (Hiroshi SHIBATA) wrote:</p>
<blockquote>
<p>This discussion started with "how big the incompatibility is?" at <a href="https://bugs.ruby-lang.org/issues/16150#note-18" class="external">https://bugs.ruby-lang.org/issues/16150#note-18</a></p>
<p>The old version of Rails is incompatible. And I also got the issue of <code>middleman</code> depends on <code>memoist</code>. So, my company blog built by <code>middleman</code> is broken now with Ruby 2.7.0-preview2.</p>
<p>We found the big incompatibility now. We should restart to discuss this feature.</p>
</blockquote>
<p>Thank you for the more detailed explanation.</p>
<p>IMHO, requiring to update (not upgrade) Rails before Ruby is good practice anyway, so the fact that 6.0.0 has an issue is unfortunate but not a deal breaker.</p>
<p>FWIW, the PR to memoist has just been merged (not yet released): <a href="https://github.com/matthewrudy/memoist/pull/82" class="external">https://github.com/matthewrudy/memoist/pull/82</a></p>
<p>I understand having to update multiple dependencies to use Ruby 2.7 is not great.</p>
<p>With this change reverted, are both cases working?</p>
<p>So I guess we should try to deprecate mutable String#to_sym via <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: eventually_frozen flag to gradually phase-in frozen strings (Closed)" href="https://bugs.ruby-lang.org/issues/16153">#16153</a> now.<br>
On the upside, it seems useful to have such a deprecation facility for making more Strings frozen.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=833572019-12-23T15:29:06ZEregon (Benoit Daloze)
<ul><li><strong>Assignee</strong> deleted (<del><i>Eregon (Benoit Daloze)</i></del>)</li></ul> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=834392019-12-27T00:13:44Zzunda (zunda an)
<ul></ul><p>With help by mame-san, I found <code>nil.to_s</code> returning a frozen empty string (<code>""</code>) broke the http gem on ruby-2.7.0. I wonder how other gems maybe affected.</p>
<p>The code raises <code>FrozenError</code> when trying to <code>force_encoding</code> after <code>read</code>ing a <code>nil</code> from the stream:<br>
<a href="https://github.com/httprb/http/blob/v3.3.0/lib/http/response/body.rb#L31" class="external">https://github.com/httprb/http/blob/v3.3.0/lib/http/response/body.rb#L31</a><br>
<a href="https://github.com/httprb/http/blob/v3.3.0/lib/http/response/body.rb#L52" class="external">https://github.com/httprb/http/blob/v3.3.0/lib/http/response/body.rb#L52</a></p>
<p><code>clear</code>ring it also records a warning:<br>
<a href="https://github.com/httprb/http/blob/v3.3.0/lib/http/response/body.rb#L53" class="external">https://github.com/httprb/http/blob/v3.3.0/lib/http/response/body.rb#L53</a></p>
<p>For now, this can be worked around with making sure to have the <code>readpartial</code> method return an unfrozen empty string:<br>
<a href="https://github.com/zunda/http/blob/870d374/lib/http/connection.rb#L96" class="external">https://github.com/zunda/http/blob/870d374/lib/http/connection.rb#L96</a><br>
<a href="https://github.com/zunda/http/compare/v3.3.0...870d37406eb5221c54f1e289ee0cf7700ba5f53b#diff-b31c2c9884d039f633a34d10a344d68b" class="external">https://github.com/zunda/http/compare/v3.3.0...870d37406eb5221c54f1e289ee0cf7700ba5f53b#diff-b31c2c9884d039f633a34d10a344d68b</a></p>
<p>matz (Yukihiro Matsumoto) wrote:</p>
<blockquote>
<p>byroot (Jean Boussier) wrote:</p>
<blockquote>
<p>I get you are worried about <code>String#to_s</code>, but what about others that have been mentioned here, namely:</p>
<ul>
<li><code>NilClass#to_s</code></li>
<li><code>TrueClass/FalseClass#to_s</code></li>
<li><code>Module#name</code></li>
</ul>
</blockquote>
<p>Ok for the experiment.</p>
<p>Matz.</p>
</blockquote> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=834462019-12-27T11:37:38ZEregon (Benoit Daloze)
<ul></ul><p>zunda (zunda an) wrote:</p>
<blockquote>
<p>For now, this can be worked around with making sure to have the <code>readpartial</code> method return an unfrozen empty string:</p>
</blockquote>
<p>Yes, that looks like a good fix to me.<br>
I would indeed expect usages of <code>read</code> and <code>readpartial</code> to assume it to return a newly-allocated mutable String.<br>
Using <code>nil#to_s</code> to generate an empty mutable String was arguably rather hacky.<br>
I'd write it like <code>chunk || +""</code> or <code>chunk || "".dup</code>.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=842202020-02-10T19:40:25ZEregon (Benoit Daloze)
<ul></ul><p>For making Symbol#to_s frozen, see <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: eventually_frozen flag to gradually phase-in frozen strings (Closed)" href="https://bugs.ruby-lang.org/issues/16153">#16153</a>.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=842212020-02-10T19:40:41ZEregon (Benoit Daloze)
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/16153">Feature #16153</a>: eventually_frozen flag to gradually phase-in frozen strings</i> added</li></ul> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=853012020-04-27T10:58:46Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>Could we consider <code>Symbol#to_s</code> retuning frozen strings again?</p>
<p>We've been running with <a href="https://github.com/Shopify/symbol-fstring" class="external">https://github.com/Shopify/symbol-fstring</a> for a about 5 months now, and I think the backward incompatibility problem is much less important now.</p>
<p>It's true that 6 months ago it was breaking a handful of very popular gems, but since then they've all been updated except grpc.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=853232020-04-29T23:24:38Zsam.saffron (Sam Saffron)sam.saffron@gmail.com
<ul></ul><p>I can confirm that Discourse bench and Discourse works with symbol-fstring today.</p>
<p>Old versions of pry were broken, but stuff seems fine now.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=864622020-07-08T19:42:39Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>I'd like to revisit this since it seems there's anecdotal evidence that returning frozen strings from Symbol#to_s is not as big a compatibility issue as thought.</p>
<p>Doing it for the next major release would make sense.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=874462020-09-04T07:33:03Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>I admit <code>Symbol#name</code> that returns a frozen string from a symbol. This can be a building block of the proposal.<br>
As a direct solution for this issue, we have to face the naming problem (as always).</p>
<p>Matz.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=875182020-09-10T10:24:53Ztom-lord (Tom Lord)lord.thom@gmail.com
<ul></ul><p>matz (Yukihiro Matsumoto) wrote in <a href="#note-68">#note-68</a>:</p>
<blockquote>
<p>I admit <code>Symbol#name</code> that returns a frozen string from a symbol. This can be a building block of the proposal.<br>
As a direct solution for this issue, we have to face the naming problem (as always).</p>
<p>Matz.</p>
</blockquote>
<p>For what it's worth, I agree with the above suggestion to try making a "breaking" change to <code>Symbol#to_s</code>.</p>
<p>Introducing a <code>Symbol#name</code> (<a href="https://github.com/ruby/ruby/pull/3514" class="external">https://github.com/ruby/ruby/pull/3514</a>) should be "Plan B", only if there's significant backlash from a release candidate.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=875202020-09-10T13:52:47Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>Honestly now that <code>Symbol#name</code> is exposed, it's very easy to simply replace <code>to_s</code> with <code>Symbol.alias_method(:to_s, :name)</code>.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=978232022-06-03T06:33:23Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/18595">Feature #18595</a>: Alias `String#-@` as `String#dedup`</i> added</li></ul> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=978252022-06-03T06:33:31Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>Probably <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Alias `String#-@` as `String#dedup` (Closed)" href="https://bugs.ruby-lang.org/issues/18595">#18595</a> addresses this request. Any opinion?</p>
<p>Matz.</p> Ruby master - Feature #16150: Add a way to request a frozen string from to_shttps://bugs.ruby-lang.org/issues/16150?journal_id=978332022-06-03T16:42:27Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> I don't think so, the request here was for all <code>to_s</code> methods, so <code>Object#to_s</code>, <code>Module#to_s</code>, <code>Symbol#to_s</code>, etc.</p>
<p>The semantic being: "I know I won't need to mutate that string, so you can give me your internal representation if you have one".</p>
<p>But in my opinion, since we have <code>Symbol#name</code>, the need is filled for the most part. It's just a bit annoying in some cases (e.g. <code>any_object.to_s</code>, like ERB), but it acheive the goal most of the time.</p>