|
| 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