-
Notifications
You must be signed in to change notification settings - Fork 1.6k
FromLiteral #143
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
FromLiteral #143
Conversation
Using integer/float literals in a similar system may be useful for, eg, bigint libraries, or various integer-like types: Ones checking overflow, range-limited, etc. |
This works for strings, but it definitely doesn't work for vector literals. The |
This also should cover numeric literals, as @mcpherrinm suggested. For string literals, the proposed trait works. For vector literals, I think a trait needs to be designed that effectively does what the |
If we would push each element onto a collection, perhaps FromVectorLiteral could take an Iterator, consuming it? |
Please use markdown syntax, or else the RFC is not rendered nicely. This includes
which renders as
(This is particularly important for anything with |
|
||
Before, this compiles: | ||
|
||
```let x = "Hello, world!".to_string();``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fences need to be on their own lines, i.e.
```
let x = "Hello, world!".to_string();
```
Markdown is hard without a preview.
@XMPPwocky How is the compiler supposed to generate an iterator? That also doesn't allow for placement new. I think it has to look something like trait FromVectorLiteral<T> {
fn init(capacity: uint) -> Self;
fn push(x: T);
} with a vector literal expression that ends up being typed as something that conforms to this protocol evaluating to the equivalent of As written this still doesn't support placement new, but that could be added once we have the capability of doing placement new. |
@kballard That looks workable. Another possibility, which translates directly to placement new, would be a method taking a size and returning a mutable slice. Of course, this burdens collections that are not contiguous in memory with the task of constructing a buffer and moving out of it, but it would be zero-cost for Vec and other contiguously-laid-out collections. |
@XMPPwocky That approach is problematic when considering how to handle task failure in the middle of evaluating the vector literal. My general feeling is we should wait and see how |
} | ||
trait FromVectorLiteral<T> { | ||
|
||
fn from_vector_literal(lit: &[T]) -> Self; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why &[T]
and &'static str
? Seems to me that either both should be 'static
or neither.
I can imagine I also agree with @kballard that something like |
👍 to @mcpherrinm's suggestion. It's quite annoying to work with bigints without this. |
@kballard I think an underlying problem here is that fixed-size vectors can't have their length erased and then be moved out of (unless you go through Box and friends, thus incurring heap allocation for no good reason); I suspect FromVectorLiteral might want to be postponed until we have something like an "owning slice" (not Box, but something that could take a pointer and length of something on the stack and pass it down to other functions). Probably postponable to 1.0 at least, since vec! is sufficient for the most common case. FromStringLiteral shouldn't be quite as much of a pain conceptually, and the "meow".to_string() menace must be stopped. |
@XMPPwocky Even without their length being erased, you can't move out of a fixed-length vector multiple times. And even if you could, that's still the wrong approach, because it requires evaluating all the expressions before trying to construct the collection, which makes it impossible to use placement-new to construct the values inside the collection. |
Many 👍s to the general idea (this is something I and probably many other people have wanted for a long time), and @mcpherrinm’s suggestion is certainly worth considering. (IMHO integer/float literals are more important/useful than vector literals.) But when I pictured custom literals, I always imagined it as being a compiler plugin (somewhat like a procedural macro). I’m not sure which is better—a trait is hugely easier to implement, but a compiler plugin is probably more flexible (and I believe it would resolve the problem of how |
As embarrassed as I am at the look of |
@bstrie: I'd prefer doing nothing to hardcoding String and Vec. It's awkward enough having Box hardcoded, and there's at least a reason for that. Having String/Vec special-cased would annoy the low-level folks (who aren't using std) and generally be impure. |
+1 to no hardcoding. |
The proper solution to moving in is |
+1 |
@eddyb As kballard mentioned when I proposed that, it is not friendly with placement new. A Vec-like interface is really growing on me, since data structures that are not contiguous in memory would work flawlessly (as would placement new), and judicious inlining should add no cost to data structures that are contiguous. |
|
||
```rust | ||
trait FromStringLiteral { | ||
fn from_string_literal(lit: &'static str) -> Self; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that this really matters in an RFC, but Rust style is four spaces, no tabs.
@XMPPwocky well, here's the perfect placement new interface: fn from_vector_literal<T, static N: uint>(f: impl FnOnce<(), [T, ..N]>) -> Self; |
C++11 uniform initialization (with initialization lists and constructors) is really useful and one of the features that still make me prefer C++ over Rust. For example consider this simple example case: std::map<std::string, std::vector<int>> playerScores = {
{"Athos", {1, 4, 2}},
{"Porthos", {}},
{"Aramis", {9, 2}},
}; What is currently the best way to define a similar data structure in Rust? Would it be nicer with this new RFC? |
With let player_scores: HashMap<String, Vec<int>> = box [
(box "Athos", box [1, 4, 2]),
(box "Porthos", box []),
(box "Aramis", box [9, 2])
]; @Ape IIUC this RFC is similar, but without the explicit As an aside, I was originally quite hyped about |
|
||
# Summary | ||
|
||
Create new traits, FromStringLiteral, FromVectorLiteral<T>. Types implementing these traits can be automatically constructed from literals automatically. Literals will effectively have their actual types inferred statically. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"automatically constructed from literals automatically"
If/when Rust has a more complex notion of phases (thought I once read @pcwalton saying he wanted to do something like in Racket) this would all need to be revised. But that is probably a long way off. Just something to keep in mind if this is post 1.0 either way. |
Conflicts: active/0000-fromliteral.md
Yes, this RFC would allow what eddyb mentioned. Note that Rust already supports the case of struct Foo {
x: [i32, ..4]
}
fn gimme_a_foo() -> Foo {
Foo {x: [1, 2, 3, 4]}
} It only works for arrays, though, because they are primitive types and have special syntax for it. This RFC extends this syntax to arbitrary types that opt-in to it. |
One thing that doesn’t appear to be covered in the RFC is how the compiler would infer the type of a string/vector literal. Would it fall back to One must also take into account what would happen in a case such as this: fn str_fn<S: Str>(s: S) { ... }
str_fn("hello world"); Today, this works, but with the proposed change, even with a fallback to |
@P1start I don't think the reason the int/float fallbacks were removed applies here. If you get the wrong numeric type in a case where the true type cannot be inferred, this is unlikely to actually fail to compile; instead it will appear to work, until you get unexpected overflow behavior at runtime. If you get an &'static str and try to call .append, that will not compile; in fact, I believe that all methods shared between &'static str and String behave identically; the same applies to [T, ..N] and Vec. I think the second case could be soluble by enhancing inferrence to better support fallbacks with generics, but it is certainly a problem. |
int/float fallbacks were removed because numeric primitives have limitations that aren't reflected in the type system, namely the bounds of the type and the resulting overflow behavior. So getting the wrong numeric type can lead to unexpected overflow. It also de-emphasizes None of that applies to values constructible from string/vector literals, so our current behavior for those literals is perfectly fine as a default. |
fn from_string_literal(lit: &'static str) -> Self; | ||
} | ||
|
||
trait FromVectorLiteral<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be called FromArrayLiteral
. Rust uses the following names:
[T, .. n]
(fixed sized) array&[T]
sliceVec<T>
vector
This kind of thing is OK in Haskell, but in Rust I think we should stick to the property that things are what they look like, i.e. what looks like a literal isn't actually a call to a user-defined function. |
@glaebhoerl |
@eddyb: At that point why not just use a macro? It doesn't work for string literals, but for e.g. |
It would be very nice if python-style Decimal could be created like:
(i.e. without turning it into floating point first, and keeping precision) |
Conversions and implicit conversions are in important ergonomic concern right now. We want to get the ergonomics here right without sacrificing Rust's other qualities like predictability. This proposal has some shortcomings that lead us to think that it alone is not the answer: it's limited to two use cases while there are potentially others, the signatures are probably not correct, interaction with the compiler is unspecified (these two traits appear to be lang items). In general we want to put more and careful thought into this subject before moving forward, and there are several major language changes in the pipeline with higher priority. Closing and postponing. |
Module Unification
literally the best RFC related to literals.