Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

copy not implemented for String #31995

Closed
meggart opened this issue May 10, 2019 · 15 comments
Closed

copy not implemented for String #31995

meggart opened this issue May 10, 2019 · 15 comments
Labels
strings "Strings!"

Comments

@meggart
Copy link
Contributor

meggart commented May 10, 2019

I just realized this on Julia 1.1 as well as master:

julia> copy("I want to be copied")
ERROR: MethodError: no method matching copy(::String)
Closest candidates are:
  copy(::Expr) at expr.jl:36
  copy(::Core.CodeInfo) at expr.jl:64
  copy(::BitSet) at bitset.jl:46
  ...
Stacktrace:
 [1] top-level scope at REPL[3]:1

I was wondering if this is desired behavior or would if it would be ok to define copy(s::String)=s since Strings are immutable.

@JeffBezanson
Copy link
Member

I've tried to avoid defining copy for immutable objects. How did this arise? My view is that code calling copy without knowing whether the argument is mutable can't really be generic.

@stevengj stevengj added the strings "Strings!" label May 10, 2019
@meggart
Copy link
Contributor Author

meggart commented May 13, 2019

I have a variable a that is either a String or a Vector{UInt8} or a HTTP reply body, which should be converted into a string without invalidating the input. Since String constructors are defined for all possible inputs I just coded a2 = String(copy(a)), and just got a bit surprised by the missing copy method.

There is some precedence for defining copy on immutables (https://github.com/JuliaLang/julia/blob/master/base/number.jl#L88). Of course having this functionality is not critical for me, my current workaround is to define

copied_string(x) = String(copy(x))
copied_string(x::String) = x

which works fine but just feels a bit too verbose. Maybe there already exists a more convenient way to achieve this?

@StefanKarpinski
Copy link
Member

I still worry that this is overly pedantic. I guess the argument is something along these lines... If you're copying something, then it must be because you're going to mutate it—otherwise why copy it? But if some of the values are immutable, then that's going to fail anyway, so what kind of code needs this?

@KristofferC
Copy link
Member

String is a bit special in that it mutates the input argument for some types.

@meggart
Copy link
Contributor Author

meggart commented May 20, 2019

Indeed, if String would copy by default and there was an additional String! as was suggested here #24388 (comment) this would solve my use case as well.

@stevengj
Copy link
Member

stevengj commented Aug 31, 2020

We already have lots of copy methods for immutable objects that return an immutable, so that cat is out of the bag:

julia> copy(3)
3

julia> copy(1:10)
1:10

On the other hand, Base.copymutable works for String:

julia> Base.copymutable("Hello")
5-element Array{Char,1}:
 'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase)
 'l': ASCII/Unicode U+006C (category Ll: Letter, lowercase)
 'l': ASCII/Unicode U+006C (category Ll: Letter, lowercase)
 'o': ASCII/Unicode U+006F (category Ll: Letter, lowercase)

(Maybe in Julia 2.0 we can make copy equivalent to copymutable, but in the present state it's not crazy to define copy(::String).)

@stevengj
Copy link
Member

stevengj commented Aug 31, 2020

For example, copy(::Number) was preserved by @JeffBezanson in #15675 even while other immutable copy methods were deleted.

@KristofferC
Copy link
Member

KristofferC commented Aug 31, 2020

so that cat is out of the bag:

I don't get what point you are trying to make. That there should be a generic Base.copy(x) = isbits(x) ? x : throw(MethodError...)?

For example, copy(::Number) was preserved by @JeffBezanson in #15675

Yes:

To minimize breakage, I'm leaving the definitions for Range and Number for now,

@stevengj
Copy link
Member

To minimize breakage

Yes, i.e. generic code was relying on those methods, so they can't have been that useless.

@KristofferC
Copy link
Member

Yes, they could have been? People do useless stuff all the time. Just look at one of the logs from PkgEval for an upcoming release.

@JeffBezanson
Copy link
Member

In this case it was a Vector that needed to be copied, not a String, so I'm inclined to close this.

I'm not a fan of the default copymutable method that calls collect. That's not really a "copy" since an array might be a very different structure than what you started with.

@stevengj
Copy link
Member

stevengj commented Aug 31, 2020

That's not really a "copy" since an array might be a very different structure than what you started with.

Which is exactly what you want if you want a mutable copy … recall that copymutable was introduced in the first place to fix methods like sort(x) = sort!(copymutable(x)) for immutable collections and iterators.

@meggart
Copy link
Contributor Author

meggart commented Sep 1, 2020

Which is exactly what you want if you want a mutable copy … recall that copymutable was introduced in the first place to fix methods like sort(x) = sort!(copymutable(x)) for immutable collections and iterators.

As stated above, in the use case I had I needed to copy an array to make sure it is not invalidated when passed to a String constructor. I agree with the reasoning that it should not be necessary to copy immutables and that the actual issue is the fact that the String constructor invalidates its input. The String docs say:

If v is Vector{UInt8} it will be truncated to zero length and
future modification of v cannot affect the contents of the resulting string.
To avoid truncation use String(copy(v)).

So it is explicitly suggested to call copy which causes the MethodError if v can be both a Vector{UInt8} or a String.

I agree we might close this issue or rename it to something Make String constructor copy by default or refer to an already existing issue that discusses this.

@stevengj
Copy link
Member

stevengj commented Sep 1, 2020

String(copy(v)) is a good use-case for adding copy(s::String) = s, in my opinion, and helps to illustrate in general why copy methods for immutables can be useful.

@JeffBezanson
Copy link
Member

The String constructor is covered by #32528 .

I don't think methods should be added for the convenience of not writing a branch when a function doesn't apply to one case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
strings "Strings!"
Projects
None yet
Development

No branches or pull requests

5 participants