rust-lang/rfcs

newtype / generalized newtype deriving

Opened this issue ยท 21 comments

Pretty

Tracking issue for postponed PR #186

An attribute like #[derive_newtype], applicable to "newtype" tuple structs like struct Wrapper(i32), would be fantastic.

reem commented

The interaction with TypeId and Any here is also of note, since an incorrect implementation of newtype deriving can cause serious problems when combined with downcasting - if get_type_id from Any is inherited such that it reports the same TypeId for the original type and the new type, bad things can be made to happen.

If it's discovered that a keyword is required to support newtypes, if the language follows semver (and I seem to recall that it does), would that require that rust be bumped to 2.0 just to add the keyword? (since it's a breaking change when existing code that uses the token "newtype" stops working.) Wouldn't it be more conservative to reserve the keyword now, and release it if we can determine that the functionality can be implemented with a macro or some such?

Afaik the official stance is that keywords can be added without bumping the major version by adding an attribute for enabling them.

Postponed RFC #949 contains a description of a possible newtype semantics.

Shouldn't something like this be a 1.0 blocker? It seems like you don't want people rolling their own solutions to get around a pain point in something as fundamental as newtype.

For example, it would be nice to be able to do checked addition, max_value(), etc... on a newtyped int without a bunch of boilerplane:

http://is.gd/7Cdzkd

Credit: this came from help I got during an IRC discussion with flan3002.

Thanks! And sorry if commenting on two related issues is considered a cross-post; not sure about ettiquet on this. This seems to be the higher priority / still open thread...

There seems to be a forwards compatibility problem with this RFC in combination with the coherence rules: even under the new rules proposed by @nikomatsakis in #1023 this would mean that a parent crate cannot add a new trait implementation to a type "Foo", without potentially breaking downstream crates, which may have newtyped "Foo" to "Bar", and then implemented that same trait for Bar.

Just to put more info into the thread: my proposal is more like the first alternative, supporting #[derive] -- is the newtype keyword even feasible post 1.0?

One ๐Ÿ‘ for #[derive] from me. Though I'm confused on what it mean to support such generalization for "all tuple types".

I opened a new RFC for this: #2242

I'm here because this would be useful for me. I want to be able to "enhance" the debug for a given type (by adding more functionality than the source crate provides), but would like all other behaviors to be transparent.

Also tossing in my voice here as my hands are tied without this feature. The newtype pattern is quite often needed to get around the orphan rule, but it's not much good if I lose all of the Traits of the type I'm wrapping.

+1 from me, I'd love to have this functionality, it would let me reuse methods from the wrapped type and avoid having to use the "Deref as newtype" anti-pattern.

I think this is one of the most important, missing features of Rust.
While this feature might seem simple it has some really great benefits like being able to circumvent orphan rules.
I often find myself having to write a lot of boilerplate in order to implement serde Serialize/Deserialize for types of crates which don't implement it. If a syntax like:

#[derive(Serialize, Deserialize)]
type ExternType as NewType;

would exist i could save a lot of boilerplate. Also it makes the whole ecosystem way more extensible and more easy to use.

@DrSloth I don't think what you describe will ever be possible. Newtype deriving generally means implementing a trait for a wrapper type in terms of the implementation of the same trait for the inner type. If the inner type doesn't implement said trait, you don't really have anything to go off for the derived trait implementation.

I guess you're imagining the Serialize / Deserialize derive macros being able to work with the original item definition, but that would break many abstraction boundaries. To even be safe and in line with semver, this would have to limited to types with no private fields, without #[non_exhaustive] and probably other attributes, and then it would still be incredibly hard or impossible to implement.

@jplatte I get what you are saying, but i think new types should be exactly that. A way to simply create a type equivalent to another one with access to private fields, trait implementations, private methods and everything else.
I don't understand why it would be hard to implement though.
If i want to implement Serialize / Deserialize for an external struct i currently have to copy the complete struct with all needed fields and then implement From and Into for it. The most problems / the most wasted time is just exactly from things like this. I feel like this is a big hinderance for the ecosystem. The main problem of a shortened syntax is that you would not be able to use field attributes.
I feel like there should be at least one 'safe' way to opt out of the orphan rules.

@jplatte I'e be curious to see an example of how the automatic new type derivation would break. I'm trying to come up with some mental examples to see how it would, but I'm unable to think of any!

@jplatte I'e be curious to see an example of how the automatic new type derivation would break. I'm trying to come up with some mental examples to see how it would, but I'm unable to think of any!

Imagine newtype-deriving Deserialize for

struct MyU32(NonZeroU32);

NonZeroU32 is itself a newtype around u32 but its u32 -> Self constructor is unsafe because that u32 must not be 0. If newtype-deriving was not limited to types with entirely public definition, Deserialize would (somehow) bypass that constructor function and you'd be able to create NonZeroU32(0u32) in safe code, which is unsound.

This kind of new type deriving would pretty much just be an abbreviation for transmute and thus raises the same safety concernes as transmute does. Unsafe shouldn't be usable in such an implicit way and it would be better to keep it explicit.

i think new types should be exactly that. A way to simply create a type equivalent to another one with access to private fields, trait implementations, private methods and everything else.

This would completely break encapsulation and expose downstream crates to unstable, non-semver implementation details of not only third-party crates, but also the standard library itself. This sounds so extremely wrong that I'm not even sure how to describe the utter terror it gives me.

With that said, I feel like this issue has gathered enough "hey, we want this too" for the time being. Everyone knows this is a useful feature, but saying that is not going to help it arrive any sooner. What we need is concrete, usable information on how it should work, and an actual implementation. Till then, saying how useful it would be won't get it anywhere. :/

(I'm aware that this is somewhat of a necro-post; honestly I think this issue should be locked until actual news comes up.)