قالب وردپرس درنا توس
Home / IOS Development / numericCast (_ :) – NSHipster

numericCast (_ :) – NSHipster



Everyone has their favorite analysis to describe programming.

It's woodworking or knitting or gardening.
Or maybe it's problem solving and stories and art.
The programming is like writing there is no doubt;
The question is whether it's poetry or prose.
And if programming is like music,
It's always jazz for whatever reason.

But perhaps the closest point of comparison for what we do all day
Coming from the Middle East peoples:
Open any edition of
Thousand and one night
( ألف ليلة وليلة )
and you will find descriptions of supernatural beings known as
jinn djinn genius or 🧞.
Whatever you call them,
You are probably familiar with his habit of giving wishes,
and the accident that inevitably causes.

In many ways,
Computers are the physical performance of metaphysical desire fulfillment.
As a genius, a computer will happily agree with what you tell it to do,
regardless of what your original purpose might have been.
And once you've realized your mistake,
It may be too late to do anything about it.

As a Swift developer,
There is a good chance that you have been affected by integer type conversion errors
and thought
"I wish these warnings would go away and my code would finally compile."

If it sounds familiar,
you would like to learn about numerically Cast (_ :) ,
a small utility in the Swift Standard Library
It might be exactly what you hoped for.
But be careful what you want,
It can only come true.


Let's start by removing any magic thinking
about what numerical Cast (_ :) turns off
BinaryInteger U : BinaryInteger 1

9659011]> [ ] x : T ) U {
Return U [19659015] x )
}

(As we learned in our article about Never ,
even the smallest amount of Swift code can have a big influence.)

BinaryInteger protocol
was introduced in Swift 4
as part of an overhaul of how the numbers work in the language.
It provides a unified interface for working with integers,
both signed and unsigned, and of all shapes and sizes.

When you convert an integer to another type,
It is possible that the value can not be represented by that type.
This happens when you try to convert a signed integer
to an unsigned integer (for example -42 as a UInt ),
or when a value exceeds the representative range of the destination type
(for example, UInt8 can only represent numbers between 0 and 255 ).

Binary Integer defines four strategies of conversion between integer types,
each with different behaviors to handle values ​​out of reach:

  • Range-Checked Conversion
    ( init (_ 🙂 ):
    Trigger a runtime error for outdoor range
  • Exact Conversion
    ( init (exact 🙂 ):
    Return null for values ​​out of reach
  • Clamping Conversion
    ( init (clip 🙂 ):
    Use closest representative value for values ​​outside values ​​
  • Bit pattern conversion
    ( init (truncatingIfNeeded 🙂 ):
    Trunker to the width of the target integer type

The correct conversion strategy
depends on the situation in which it is used.
Sometimes it is desirable to squeeze values ​​into a representative area;
Sometimes it's better not to get any value at all.
In the case of numerical Cast (_ :) ,
Site-controlled conversion is used for convenience.
The downside is that
calls this function without values
causing a runtime error
(specifically, it falls on overflow in -O and -Onone ).

Thinking literally, thinking critically

Before moving on,
Let's take a moment to talk about integer letters.

As we have discussed in previous articles,
Swift provides a convenient and expandable way to represent values ​​in source code.
When used in combination with the language's use of typewriters,
things often "just work"
… that's nice and everything but can be confusing when things just do not.

Consider the following example
in which arrays of signed and unsigned integers
initiated from identical literal values:

  la    arrayOfInt :   [  Int ]    =   [  1     2 [19659018]  3 ] 
  la    arrayOfUInt :   [  UINT ]    =   [  1 [19659015]   2     3 ] 

Despite their apparent equivalence,
For example, we can not do this:

  arrayOfInt    as   [  UInt ]    // Error: Can not Convert Value of Type & # 39; [Int] & # 39; to write & # 39; [UInt] & # 39; in force 

One way to unite this problem with
would be to send numerical Cast function as an argument to map (_ :) :

  arrayOfInt .   map   19659021] numericCast )    as   [  UInt ] 

This corresponds to sending UInt range-controlled initialization direct:

  ] arrayOfInt [19659011].     (  UInt .   init ) 

But let's take a look at that example,
This time uses slightly different values: ] arrayOfNegativeInt : [ Int ] = [ 1 , 2 -. 3 ]
arrayOfNegativeInt map [ numericCast ) [19659080] as [ UInt ] // 🧞 Fatal error: Negative value is not representative

As a run-time approximation of compilation time
numerical Cast (_ :) is closer to as! than as or as .

Compare this to what happens if you instead pass
] arrayOfNegativeInt : [ Int ] = 1 ] 2 -. 3 ]
arrayOfNegativeInt map [19659015] UInt . init ( exact :)) // [nil, nil, nil]

numerical Cast (_ :) as its underlying range-controlled conversion,
is a dumb instrument,
and it is important to understand which deviations you make
when you decide to use it.

The cost to be right

In Swift,
The general tutorial is to use Int for integer values
(and Double for floating point values)
unless there is a really good reason to use a more specific type.
Although counts of a Collection is nonnegative by definition,
we use Int instead of UInt
because the cost of going back and forth between types
when you interact with other APIs
outweigh the potential benefit of a more precise type.
For the same reason,
It is almost always better to represent even small numbers,
as weekday figures,
with an Int ,
despite the fact that any possible value would fit into an 8-bit integer
with plenty of room to spare.

The best argument for this exercise
is a 5-minute conversation with a Swift C-API.

Older and lower level C APIs are becoming
architecture-dependent type definitions
and fine tuned value storage.
On their own they are manageable.
But on top of all other interoperability issues
as headers to pointers,
they can be a breakpoint for some
(and I do not mean the troubling type).

numerical Cast (_ :) is there for when you're tired of seeing red
and just want things to compile.

Random Collaborative Acts

The example in the official documents
should be known to many of us:

Before SE-0202,
standard practice for generating numbers in swift (on apple platforms)
involved imports of the Darwin framework
and call the feature arc4random_uniform (3) :

  uint32_t   arc4random_uniform   (  uint32_t   __ upper_bound ) 

arc4random does not require one, but two separate types of conversions in Swift:
first for the upper boundary parameter ( Int UInt32 )
and other for return value ( UInt32 Int ):

  Import    Darwin 

  Func    Random   (  in   ] :   :       [>19659013]    [

] [

]

Int arc4random_uniform ( uint32 ( vary . telle )) + vary . ]

Using numerical Cast (_ :) we can make things a bit more readable,
Func Random ( in Area : Area < Int ] -> Int {
return numericCast ( arc4random_uniform ( numericCast ([19659016] range . count )) + area . lower bound
}

numerical Cast (_ :) isn I do nothing here
which could not otherwise be achieved with type-appropriate initiators.
Instead, it acts as an indicator
that the conversion is perfunctory –
the minimum of what is needed to get the code to compile.

But as we have learned from our purchases with geniuses,
We should be careful about what we want.

Upon further inspection,
It is clear that the example the use of numerical Cast (_ :) has a critical error:
[nil, nil, nil]

] : 0 .. < 0x1_0000_0000 [nil, nil, nil]

19659015]) // 🧞 Fatal error: Not enough bits to represent it passed the value

If we look at the Standard Library's implementation
which lets us now do Int.random (in: 0 ... 10) ,
We want to see that it uses clamp, rather than range-controlled conversion.
And instead of delegating to a practical feature like arc4random_uniform
It fills values ​​from a buffer with random switches.


Getting code to compile is different than doing things right.
But sometimes it takes the former to come to the latter.
When used judiciously,
numerical Cast (_ :) is a handy tool for solving problems quickly.
It also has the added benefit of
signals potential misconduct more clearly than
A conventional type initializes.

Finally, the programming is about describing exactly what we want –
often with careful detail.
There is no genius equivalent CPU instruction for "Do the right one"
(and although it was,
would we really trust it?)
Fortunately for us,
Swift allows us to do this in a way that is
safer and more consistent than many other languages.
And honestly, who can wish for something more?


Source link

Share