The following Rust code compiles and runs without any issues.
fn main() {
    let text = \"abc\";
    println!(\"{}\", text.split(\' \').take(2).count());
}
         
        I've found it useful to let the compiler guide me:
fn to_words(text: &str) { // Note no return type
    text.split(' ')
}
Compiling gives:
error[E0308]: mismatched types
 --> src/lib.rs:5:5
  |
5 |     text.split(' ')
  |     ^^^^^^^^^^^^^^^ expected (), found struct `std::str::Split`
  |
  = note: expected type `()`
             found type `std::str::Split<'_, char>`
help: try adding a semicolon
  |
5 |     text.split(' ');
  |                    ^
help: try adding a return type
  |
3 | fn to_words(text: &str) -> std::str::Split<'_, char> {
  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Following the compiler's suggestion and copy-pasting that as my return type (with a little cleanup):
use std::str;
fn to_words(text: &str) -> str::Split<'_, char> {
    text.split(' ')
}
The problem is that you cannot return a trait like Iterator because a trait doesn't have a size. That means that Rust doesn't know how much space to allocate for the type. You cannot return a reference to a local variable, either, so returning &dyn Iterator is a non-starter. 
As of Rust 1.26, you can use impl trait:
fn to_words<'a>(text: &'a str) -> impl Iterator<Item = &'a str> {
    text.split(' ')
}
fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}
There are restrictions on how this can be used. You can only return a single type (no conditionals!) and it must be used on a free function or an inherent implementation.
If you don't mind losing a little bit of efficiency, you can return a Box<dyn Iterator>:
fn to_words<'a>(text: &'a str) -> Box<dyn Iterator<Item = &'a str> + 'a> {
    Box::new(text.split(' '))
}
fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}
This is the primary option that allows for dynamic dispatch. That is, the exact implementation of the code is decided at run-time, rather than compile-time. That means this is suitable for cases where you need to return more than one concrete type of iterator based on a condition.
use std::str;
struct Wrapper<'a>(str::Split<'a, char>);
impl<'a> Iterator for Wrapper<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        self.0.next()
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}
fn to_words(text: &str) -> Wrapper<'_> {
    Wrapper(text.split(' '))
}
fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}
As pointed out by reem
use std::str;
type MyIter<'a> = str::Split<'a, char>;
fn to_words(text: &str) -> MyIter<'_> {
    text.split(' ')
}
fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}
When impl Trait isn't available for use, closures make things more complicated. Closures create anonymous types and these cannot be named in the return type:
fn odd_numbers() -> () {
    (0..100).filter(|&v| v % 2 != 0)
}
found type `std::iter::Filter<std::ops::Range<{integer}>, [closure@src/lib.rs:4:21: 4:36]>`
In certain cases, these closures can be replaced with functions, which can be named:
fn odd_numbers() -> () {
    fn f(&v: &i32) -> bool {
        v % 2 != 0
    }
    (0..100).filter(f as fn(v: &i32) -> bool)
}
found type `std::iter::Filter<std::ops::Range<i32>, for<'r> fn(&'r i32) -> bool>`
And following the above advice:
use std::{iter::Filter, ops::Range};
type Odds = Filter<Range<i32>, fn(&i32) -> bool>;
fn odd_numbers() -> Odds {
    fn f(&v: &i32) -> bool {
        v % 2 != 0
    }
    (0..100).filter(f as fn(v: &i32) -> bool)
}
If you need to conditionally choose an iterator, refer to Conditionally iterate over one of several possible iterators.