C++20 introduced two new methods on std::string and std::string_view: starts_with and ends_with. These two methods do exactly what their name implies: they check wether a string starts or ends with a given prefix or suffix.

Until now, many roundabount ways were used to do this operation:

// 1.
haystack.find("needle") == 0;

// 2.
haystack.rfind("needle") == 0;

// 3.
haystack.compare(0, strlen("needle"), "needle") == 0;

// 4.
strncmp(haystack.c_str(), "needle", strlen("needle")) == 0;

// 5.
haystack.compare(haystack.length() - needle.length(), needle.length(), needle) == 0;

// 6.
std::equal(needle.rbegin(), needle.rend(), haystack.rbegin());

// 7.
haystack.rfind(needle) == (haystack.size() - needle.size());

Cases 1 through 4 can be improved using starts_with, cases 5 through 7 using ends_with.

While some cases are just less readable ways of expressing the operation, some also introduce a performance issue: there is no need to search the whole string if we can find early that there is no match!


Clang-Tidy has a related check, which was written before the introduction of those functions: abseil-string-find-startswith. This check replaces example cases 1 and 2 above with the library function absl::StartsWith. It used to only work with std::string; I added support for std::string_view.

Introducing modernize-use-starts-ends-with 🎉

screenshot of Clang-Tidy documentation

I wrote a new check, modernize-use-starts-ends-with, which has been abailable in Clang-Tidy as of version 18.1. It works on any object having the necessary starts_with and ends_with methods, including std::string and std::string_view, but also custom string types such as llvm::StringRef or folly::StringPiece. It currently covers cases 1 through 3, with improvements planned in the future, and can be run as a codemod to fix any existing cases.