Skip to content

Commit cf20ff9

Browse files
authored
Merge pull request #2306 from Centril/rfc/convert_id
RFC: Add `pub fn identity<T>(x: T) -> T { x }` to core::convert
2 parents 21f887f + ca0e063 commit cf20ff9

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed

text/2306-convert-id.md

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
- Feature Name: `convert_identity`
2+
- Start Date: 2018-01-19
3+
- RFC PR: [rust-lang/rfcs#2306](https://github.com/rust-lang/rfcs/pull/2306)
4+
- Rust Issue: [rust-lang/rust#53500](https://github.com/rust-lang/rust/issues/53500)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Adds an identity function `pub const fn identity<T>(x: T) -> T { x }`
10+
as `core::convert::identity`. The function is also re-exported to
11+
`std::convert::identity`.
12+
13+
# Motivation
14+
[motivation]: #motivation
15+
16+
## The identity function is useful
17+
18+
While it might seem strange to have a function that just returns back the input,
19+
there are some cases where the function is useful.
20+
21+
### Using `identity` to do nothing among a collection of mappers
22+
23+
When you have collections such as maps or arrays of mapping functions like
24+
below and you watch to dispatch to those you sometimes need the identity
25+
function as a way of not transforming the input. You can use the identity
26+
function to achieve this.
27+
28+
```rust
29+
// Let's assume that this and other functions do something non-trivial.
30+
fn do_interesting_stuff(x: u32) -> u32 { .. }
31+
32+
// A dispatch-map of mapping functions:
33+
let mut map = HashMap::new();
34+
map.insert("foo", do_interesting_stuff);
35+
map.insert("bar", other_stuff);
36+
map.insert("baz", identity);
37+
```
38+
39+
### Using `identity` as a no-op function in a conditional
40+
41+
This reasoning also applies to simpler yes/no dispatch as below:
42+
43+
```rust
44+
let mapper = if condition { some_manipulation } else { identity };
45+
46+
// do more interesting stuff inbetween..
47+
48+
do_stuff(42);
49+
```
50+
51+
### Using `identity` to concatenate an iterator of iterators
52+
53+
We can use the identity function to concatenate an iterator of iterators
54+
into a single iterator.
55+
56+
```rust
57+
let vec_vec = vec![vec![1, 3, 4], vec![5, 6]];
58+
let iter_iter = vec_vec.into_iter().map(Vec::into_iter);
59+
let concatenated = iter_iter.flat_map(identity).collect::<Vec<_>>();
60+
assert_eq!(vec![1, 3, 4, 5, 6], concatenated);
61+
```
62+
63+
While the standard library has recently added `Iterator::flatten`,
64+
which you should use instead, to achieve the same semantics, similar situations
65+
are likely in the wild and the `identity` function can be used in those cases.
66+
67+
### Using `identity` to keep the `Some` variants of an iterator of `Option<T>`
68+
69+
We can keep all the maybe variants by simply `iter.filter_map(identity)`.
70+
71+
```rust
72+
let iter = vec![Some(1), None, Some(3)].into_iter();
73+
let filtered = iter.filter_map(identity).collect::<Vec<_>>();
74+
assert_eq!(vec![1, 3], filtered);
75+
```
76+
77+
### To be clear that you intended to use an identity conversion
78+
79+
If you instead use a closure as in `|x| x` when you need an
80+
identity conversion, it is less clear that this was intentional.
81+
With `identity`, this intent becomes clearer.
82+
83+
## The `drop` function as a precedent
84+
85+
The `drop` function in `core::mem` is defined as `pub fn drop<T>(_x: T) { }`.
86+
The same effect can be achieved by writing `{ _x; }`. This presents us
87+
with a precendent that such trivial functions are considered useful and
88+
includable inside the standard library even though they can be written easily
89+
inside a user's crate.
90+
91+
## Avoiding repetition in user crates
92+
93+
Here are a few examples of the identity function being defined and used:
94+
95+
+ https://docs.rs/functils/0.0.2/functils/fn.identity.html
96+
+ https://docs.rs/tool/0.2.0/tool/fn.id.html
97+
+ https://github.com/hephex/api/blob/ef67b209cd88d0af40af10b4a9f3e0e61a5924da/src/lib.rs
98+
99+
There's a smattering of more examples. To reduce duplication,
100+
it should be provided in the standard library as a common place it is defined.
101+
102+
## Precedent from other languages
103+
104+
There are other languages that include an identity function in
105+
their standard libraries, among these are:
106+
107+
+ [Haskell](http://hackage.haskell.org/package/base-4.10.1.0/docs/Prelude.html#v:id), which also exports this to the prelude.
108+
+ [Scala](https://www.scala-lang.org/api/current/scala/Predef$.html#identity[A](x:A):A), which also exports this to the prelude.
109+
+ [Java](https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#identity--), which is a widely used language.
110+
+ [Idris](https://www.idris-lang.org/docs/1.0/prelude_doc/docs/Prelude.Basics.html), which also exports this to the prelude.
111+
+ [Ruby](http://ruby-doc.org/core-2.5.0/Object.html#method-i-itself), which exports it to what amounts to the top type.
112+
+ [Racket](http://docs.racket-lang.org/reference/values.html)
113+
+ [Julia](https://docs.julialang.org/en/release-0.4/stdlib/base/#Base.identity)
114+
+ [R](https://stat.ethz.ch/R-manual/R-devel/library/base/html/identity.html)
115+
+ [F#](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/operators.id%5B%27t%5D-function-%5Bfsharp%5D)
116+
+ [Clojure](https://clojuredocs.org/clojure.core/identity)
117+
+ [Agda](http://www.cse.chalmers.se/~nad/repos/lib/src/Function.agda)
118+
+ [Elm](http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#identity)
119+
120+
# Guide-level explanation
121+
[guide-level-explanation]: #guide-level-explanation
122+
123+
An identity function is a mapping of one type onto itself such that the output
124+
is the same as the input. In other words, a function `identity : T -> T` for
125+
some type `T` defined as `identity(x) = x`. This RFC adds such a function for
126+
all `Sized` types in Rust into libcore at the module `core::convert` and
127+
defines it as:
128+
129+
```rust
130+
pub const fn identity<T>(x: T) -> T { x }
131+
```
132+
133+
This function is also re-exported to `std::convert::identity`.
134+
135+
It is important to note that the input `x` passed to the function is
136+
moved since Rust uses move semantics by default.
137+
138+
# Reference-level explanation
139+
[reference-level-explanation]: #reference-level-explanation
140+
141+
An identity function defined as `pub const fn identity<T>(x: T) -> T { x }`
142+
exists as `core::convert::identity`. The function is also re-exported as
143+
`std::convert::identity`-
144+
145+
Note that the identity function is not always equivalent to a closure
146+
such as `|x| x` since the closure may coerce `x` into a different type
147+
while the identity function never changes the type.
148+
149+
# Drawbacks
150+
[drawbacks]: #drawbacks
151+
152+
It is already possible to do this in user code by:
153+
154+
+ using an identity closure: `|x| x`.
155+
+ writing the `identity` function as defined in the RFC yourself.
156+
157+
These are contrasted with the [motivation] for including the function
158+
in the standard library.
159+
160+
# Rationale and alternatives
161+
[alternatives]: #alternatives
162+
163+
The rationale for including this in `convert` and not `mem` is that the
164+
former generally deals with conversions, and identity conversion" is a used
165+
phrase. Meanwhile, `mem` does not relate to `identity` other than that both
166+
deal with move semantics. Therefore, `convert` is the better choice. Including
167+
it in `mem` is still an alternative, but as explained, it isn't fitting.
168+
169+
Naming the function `id` instead of `identity` is a possibility.
170+
This name is however ambiguous with *"identifier"* and less clear
171+
wherefore `identifier` was opted for.
172+
173+
# Unresolved questions
174+
[unresolved]: #unresolved-questions
175+
176+
There are no unresolved questions.
177+
178+
# Possible future work
179+
180+
A previous iteration of this RFC proposed that the `identity` function
181+
should be added to prelude of both libcore and libstd.
182+
However, the library team decided that for the time being, it was not sold on
183+
this inclusion. As we gain usage experience with using this function,
184+
it is possible to revisit this in the future if the team chances its mind.
185+
186+
The section below details, for posterity,
187+
the argument for inclusion that was previously in the [motivation].
188+
189+
## The case for inclusion in the prelude
190+
191+
Let's compare the effort required, assuming that each letter
192+
typed has a uniform cost with respect to effort.
193+
194+
```rust
195+
use std::convert::identity; iter.filter_map(identity)
196+
197+
fn identity<T>(x: T) -> T { x } iter.filter_map(identity)
198+
199+
iter.filter_map(::std::convert::identity)
200+
201+
iter.filter_map(identity)
202+
```
203+
204+
Comparing the length of these lines, we see that there's not much difference in
205+
length when defining the function yourself or when importing or using an absolute
206+
path. But the prelude-using variant is considerably shorter. To encourage the
207+
use of the function, exporting to the prelude is therefore a good idea.
208+
209+
In addition, there's an argument to be made from similarity to other things in
210+
`core::convert` as well as `drop` all of which are in the prelude. This is
211+
especially relevant in the case of `drop` which is also a trivial function.

0 commit comments

Comments
 (0)