diff --git a/include/common.hpp b/include/common.hpp
index 1a4c0c6e..65a7faa6 100644
--- a/include/common.hpp
+++ b/include/common.hpp
@@ -23,7 +23,6 @@
 POLYBAR_NS
 
 using std::string;
-using std::stringstream;
 using std::size_t;
 using std::move;
 using std::forward;
diff --git a/include/utils/string.hpp b/include/utils/string.hpp
index ffecf688..df56611c 100644
--- a/include/utils/string.hpp
+++ b/include/utils/string.hpp
@@ -1,11 +1,61 @@
 #pragma once
 
 #include <sstream>
+#include <cstring>
 
 #include "common.hpp"
 
 POLYBAR_NS
 
+namespace {
+  /**
+   * Overload that allows sub-string removal at the end of given string
+   */
+  inline string& operator-(string& a, const string& b) {
+    if (a.size() >= b.size() && a.substr(a.size() - b.size()) == b) {
+      return a.erase(a.size() - b.size());
+    } else {
+      return a;
+    }
+  }
+
+  /**
+   * Overload that allows sub-string removal at the end of given string
+   */
+  inline void operator-=(string& a, const string& b) {
+    if (a.size() >= b.size() && a.substr(a.size() - b.size()) == b) {
+      a.erase(a.size() - b.size());
+    }
+  }
+}
+
+class stringstream {
+ public:
+  stringstream() : m_stream() {}
+
+  template <typename T>
+  stringstream& operator<<(const T& object) {
+    m_stream << object;
+    return *this;
+  }
+
+  stringstream& operator<<(const char* cz) {
+    m_stream << cz;
+    return *this;
+  }
+
+  operator string() const {
+    return m_stream.str();
+  }
+
+  const string to_string() const {
+    return m_stream.str();
+  }
+
+ private:
+  std::stringstream m_stream;
+};
+
 namespace string_util {
   /**
    * Hash type
@@ -42,7 +92,6 @@ namespace string_util {
   string filesize_gb(unsigned long long kbytes, size_t precision = 0, const string& locale = "");
   string filesize(unsigned long long bytes, size_t precision = 0, bool fixed = false, const string& locale = "");
 
-  string from_stream(const std::basic_ostream<char>& os);
   hash_type hash(const string& src);
 }
 
diff --git a/src/adapters/net.cpp b/src/adapters/net.cpp
index cd24c591..992e0579 100644
--- a/src/adapters/net.cpp
+++ b/src/adapters/net.cpp
@@ -193,8 +193,8 @@ namespace net {
       suffixes.pop_back();
     }
 
-    return string_util::from_stream(stringstream() << std::setw(minwidth) << std::setfill(' ') << std::setprecision(0)
-                                                   << std::fixed << speedrate << " " << suffix << "/s");
+    return stringstream() << std::setw(minwidth) << std::setfill(' ') << std::setprecision(0) << std::fixed << speedrate
+                          << " " << suffix << "/s";
   }
 
   // }}}
diff --git a/src/utils/http.cpp b/src/utils/http.cpp
index 2bd3fe95..365dd3be 100644
--- a/src/utils/http.cpp
+++ b/src/utils/http.cpp
@@ -24,7 +24,7 @@ http_downloader::~http_downloader() {
 }
 
 string http_downloader::get(const string& url) {
-  stringstream out{};
+  std::stringstream out{};
   curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
   curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &out);
 
@@ -44,7 +44,7 @@ long http_downloader::response_code() {
 
 size_t http_downloader::write(void* p, size_t size, size_t bytes, void* stream) {
   string data{static_cast<const char*>(p), size * bytes};
-  *(static_cast<stringstream*>(stream)) << data << '\n';
+  *(static_cast<std::stringstream*>(stream)) << data << '\n';
   return size * bytes;
 }
 
diff --git a/src/utils/string.cpp b/src/utils/string.cpp
index 29827e24..14ad7220 100644
--- a/src/utils/string.cpp
+++ b/src/utils/string.cpp
@@ -161,7 +161,7 @@ namespace string_util {
    */
   vector<string>& split_into(const string& s, char delim, vector<string>& container) {
     string str;
-    stringstream buffer(s);
+    std::stringstream buffer(s);
     while (getline(buffer, str, delim)) {
       container.emplace_back(str);
     }
@@ -191,7 +191,7 @@ namespace string_util {
    * Create a floating point string
    */
   string floating_point(double value, size_t precision, bool fixed, const string& locale) {
-    stringstream ss;
+    std::stringstream ss;
     ss.imbue(!locale.empty() ? std::locale(locale.c_str()) : std::locale::classic());
     ss << std::fixed << std::setprecision(precision) << value;
     return fixed ? ss.str() : replace(ss.str(), ".00", "");
@@ -225,18 +225,6 @@ namespace string_util {
     return floating_point(value, precision, fixed, locale) + " GB";
   }
 
-  /**
-   * Get the resulting string from a ostream/
-   *
-   * Example usage:
-   * @code cpp
-   *   string_util::from_stream(stringstream() << ...);
-   * @endcode
-   */
-  string from_stream(const std::basic_ostream<char>& os) {
-    return dynamic_cast<const stringstream&>(os).str();
-  }
-
   /**
    * Compute string hash
    */
diff --git a/src/x11/connection.cpp b/src/x11/connection.cpp
index 383153e9..e75328b7 100644
--- a/src/x11/connection.cpp
+++ b/src/x11/connection.cpp
@@ -107,7 +107,7 @@ Visual* connection::visual(uint8_t depth) {
  * Create X window id string
  */
 string connection::id(xcb_window_t w) const {
-  return string_util::from_stream(std::stringstream() << "0x" << std::hex << std::setw(7) << std::setfill('0') << w);
+  return stringstream() << "0x" << std::hex << std::setw(7) << std::setfill('0') << w;
 }
 
 /**
diff --git a/tests/unit_tests/utils/string.cpp b/tests/unit_tests/utils/string.cpp
index aebd4ce7..2e13b2c8 100644
--- a/tests/unit_tests/utils/string.cpp
+++ b/tests/unit_tests/utils/string.cpp
@@ -72,13 +72,6 @@ int main() {
     expect(string_util::find_nth("foobarfoobar", 0, "o", 3) == size_t{7});
   };
 
-  "from_stream"_test = [] {
-    auto result =
-        string_util::from_stream(std::stringstream() << std::setw(6) << std::setfill('z') << "foo"
-                                                     << "bar");
-    expect(result == "zzzfoobar");
-  };
-
   "hash"_test = [] {
     unsigned long hashA1{string_util::hash("foo")};
     unsigned long hashA2{string_util::hash("foo")};
@@ -106,4 +99,20 @@ int main() {
     expect(string_util::filesize_gb(3 * 1024 * 1024 + 800 * 1024) == "4 GB");
     expect(string_util::filesize(3 * 1024 * 1024) == "3 GB");
   };
+
+  "stringstream"_test = [] {
+    string s;
+    expect((s = (stringstream() << "test")) == "test"s);
+    expect((s = (stringstream() << std::setprecision(2) << std::fixed << 1.25)).erase(0, 2) == "25"s);
+  };
+
+  "operators"_test = [] {
+    string foo = "foobar";
+    expect(foo - "bar" == "foo");
+    string baz = "bazbaz";
+    expect(baz - "ba" == "bazbaz");
+    expect(baz - "bazbz" == "bazbaz");
+    string aaa = "aaa";
+    expect(aaa - "aaaaa" == "aaa");
+  };
 }