The notation $f:A\to B$ means that $f$ is a function which has $A$ as domain and $B$ as codomain.
As for why we would feel like we need to specify a codomain when range does the job, that's a bit more tricky. The short answer is that it's generally easier to find an appropriate codomain than the range, and it's also often easier to write.
As an example, just take $f(x) = x+\frac 1x$, with the domain $\Bbb R\setminus \{0\}$. Clearly, $\Bbb R$ is a codomain, and that's that. What's the range? You might already know that it's $(-\infty, -2]\cup[2, \infty)$, but if you don't, it's not trivial to find, and it's a hassle to write.
Furthermore, what do we actually need it for? The codomain / range is relevant, for instance, when composing functions. For instance, calculating $f(f(x))$. In that case it might be fitting to use $\Bbb R\setminus \{0\}$ as the codomain, but once you do that, you can see that the domain and codomain are equal so composing the function with itself is unproblematic.
(Other answers have pointed out the relatively common practice of caring about all functions with a given codomain, which is a lot easier to write if we have notation which specifies codomain rather than range. Most of the time we're doing this, we don't really care whether those functions are surjective.)
It just turns out that the exact range of a function isn't that important. We need to know that the operations we intend to do with the output of the function are valid, and that's that. So when we can get away with a simpler and less strict codomain, then that is what we do.
Of course, saying that $f:\Bbb R\setminus \{0\}\to \Bbb C$ seems a bit silly too. So there is some merit to restricting the codomain, at least a little bit.
There is a parallel in programming. If you're familiar with most common compiled languages, then you have probably seen something like
int someFunction(float a) {
...
}
where we then know that the codomain of that function is $\Bbb Z$ (within machine limits, of course). It's often not that important to know exactly which integers could possibly come as a result of that function (you just trust that the function is correctly implemented, and that's that). But the fact that it only gives back integers is important, especially to the compiler, where a line like someFunction(3.2)+someFunction(4.1) becomes widely different machine instructions if the codomain is suddenly changed to float.
On the other hand, if you had some way of telling the compiler that the function will only give even numbers, or only perfect squares, then that probably isn't of any help to the compiler at all. (Although compilers are really clever things; I've seen them recognize several different implementations of $\sum_{i = 1}^n i$ and actually spit out machine instructions to $\frac{n(n+1)}2$, so they might be able to squeeze out a nanosecond anyway.)