You don't call <T as StringUtils>::remove_edges but <&str as StringUtils>::remove_edges in your split_in_middle method when you use self.as_ref() instead of self itself. AsRef::as_ref doesn't return a reference that lives as long as the implementing reference type, but instead creates a new temporary reference that lives only till the end of split_in_middle. Your test works fine when I replace the temporary &str reference as the receiver of remove_edges with self.
Edit: I think my reasoning as to what causes the compiler error was wrong. It is not the lifetime of the &str reference returned from calling self.as_ref(), but I think the problem is a double borrow &'temporary &'a str. The outer &'temporary reference is created during method call resolution of remove_edges. The outer temporary reference is needed because only &'a str implements remove_edges, not str itself. If we add an implementation of StringUtils for str, we can pass the &'a str instance directly to remove_edges, without having to create a temporary reference. Here an example of what I mean, I'm quite unsatisfied with my explanation, I hope the code makes more sense.
When str implements the trait, you can call the methods in &self == &str. Without it, it's still implemented for &str, so it automatically adds a reference indirection and you'll call on &self == &&str.
To understand why it calls the method on &str vs &&str, you'll need to know about traits in general, look at the existing implementations of AsRef (in particular also this one) and understand the basics of dynamically sized types and of method resolution. To understand why the double indirection breaks the lifetimes, understanding lifetime elision rules might help. As you see by these links, for a full picture of advanced features, besides the book, useful resources can be the standard library docs, the nomicon, and the reference.