You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by ad...@apache.org on 2020/04/04 00:47:28 UTC

[kudu] branch master updated: remove last vestiges of boost::bind, boost::function, and std::bind

This is an automated email from the ASF dual-hosted git repository.

adar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new 5739add  remove last vestiges of boost::bind, boost::function, and std::bind
5739add is described below

commit 5739add189361ddf0607df84c258c542159719e8
Author: Adar Dembo <ad...@cloudera.com>
AuthorDate: Tue Mar 31 17:57:11 2020 -0700

    remove last vestiges of boost::bind, boost::function, and std::bind
    
    With this patch, all Kudu functors use std::function, and all capturing or
    binding is done via C++ lambdas.
    
    The lack of support for capture-via-move rears its ugly head in
    raft_consensus.cc, but I verified that this only leads to inefficiency, not
    incorrectness (i.e. lifecycle issues). C++14 (or 17) support can't come soon
    enough!
    
    Change-Id: Ifd876f200f680f3d3617d78852f1e0a9c5649372
    Reviewed-on: http://gerrit.cloudera.org:8080/15639
    Tested-by: Kudu Jenkins
    Reviewed-by: Alexey Serbin <as...@cloudera.com>
    Reviewed-by: Andrew Wong <aw...@cloudera.com>
---
 src/kudu/benchmarks/tpch/rpc_line_item_dao-test.cc |  11 +--
 src/kudu/benchmarks/tpch/rpc_line_item_dao.cc      |   6 +-
 src/kudu/benchmarks/tpch/rpc_line_item_dao.h       |  10 +-
 src/kudu/benchmarks/tpch/tpch1.cc                  |  11 ++-
 src/kudu/benchmarks/tpch/tpch_real_world.cc        |   6 +-
 src/kudu/cfile/encoding-test.cc                    |   6 +-
 src/kudu/client/client-internal.cc                 |  50 +++++-----
 src/kudu/client/client-internal.h                  |  76 +++++++-------
 src/kudu/client/client-test.cc                     |  51 +++++-----
 src/kudu/client/client-unittest.cc                 |   7 +-
 src/kudu/client/client.cc                          |  12 +--
 src/kudu/client/master_proxy_rpc.cc                |   3 +-
 src/kudu/client/master_rpc.cc                      |  10 +-
 src/kudu/client/master_rpc.h                       |   2 +-
 src/kudu/client/session-internal.cc                |   9 +-
 src/kudu/consensus/consensus-test-util.h           |  22 ++---
 src/kudu/consensus/leader_election-test.cc         |  24 ++---
 src/kudu/consensus/raft_consensus.cc               |  61 ++++++------
 src/kudu/integration-tests/linked_list-test-util.h |  83 ++++++++--------
 src/kudu/integration-tests/linked_list-test.cc     |  16 ++-
 .../integration-tests/tablet_replacement-itest.cc  |   3 +-
 src/kudu/integration-tests/token_signer-itest.cc   |  13 +--
 src/kudu/master/catalog_manager.cc                 |  31 +++---
 src/kudu/master/master-test.cc                     |   3 +-
 src/kudu/master/master_path_handlers.cc            |  22 +++--
 src/kudu/rpc/messenger.cc                          |   8 +-
 src/kudu/rpc/messenger.h                           |   8 +-
 src/kudu/rpc/negotiation-test.cc                   | 110 ++++++++++++---------
 src/kudu/rpc/proxy.cc                              |   5 +-
 src/kudu/rpc/reactor-test.cc                       |  18 ++--
 src/kudu/rpc/reactor.cc                            |  59 +++++------
 src/kudu/rpc/reactor.h                             |  50 +++++-----
 src/kudu/rpc/response_callback.h                   |   4 +-
 src/kudu/rpc/retriable_rpc.h                       |   5 +-
 src/kudu/rpc/rpc-bench.cc                          |   3 +-
 src/kudu/rpc/rpc-test.cc                           |  16 +--
 src/kudu/rpc/rpc.cc                                |   5 +-
 src/kudu/rpc/rpc.h                                 |   1 +
 src/kudu/rpc/rpc_stub-test.cc                      |  21 ++--
 src/kudu/security/init.cc                          |   2 +-
 src/kudu/server/default_path_handlers.cc           |   7 +-
 src/kudu/server/rpcz-path-handler.cc               |  12 ++-
 src/kudu/server/server_base.cc                     |   5 +-
 src/kudu/server/tracing_path_handlers.cc           |   9 +-
 src/kudu/server/webserver.cc                       |  14 ++-
 src/kudu/tablet/tablet_replica.cc                  |   5 +-
 src/kudu/tablet/transactions/transaction_driver.cc |   4 +-
 src/kudu/tools/ksck_remote-test.cc                 |   2 -
 src/kudu/tools/ksck_remote.cc                      |  10 +-
 src/kudu/tools/tool_action_common.cc               |  53 +++++-----
 src/kudu/tools/tool_action_common.h                |  15 +--
 src/kudu/tserver/tablet_server-test.cc             |   3 +-
 src/kudu/tserver/tablet_service.cc                 |  25 ++---
 src/kudu/tserver/tserver_path_handlers.cc          |  41 +++++---
 src/kudu/util/debug/trace_event_impl.cc            |   2 +-
 src/kudu/util/url-coding.cc                        |   6 +-
 src/kudu/util/web_callback_registry.h              |   8 +-
 57 files changed, 528 insertions(+), 556 deletions(-)

diff --git a/src/kudu/benchmarks/tpch/rpc_line_item_dao-test.cc b/src/kudu/benchmarks/tpch/rpc_line_item_dao-test.cc
index dbba1f1..4af6d0f 100644
--- a/src/kudu/benchmarks/tpch/rpc_line_item_dao-test.cc
+++ b/src/kudu/benchmarks/tpch/rpc_line_item_dao-test.cc
@@ -19,13 +19,12 @@
 
 #include <algorithm>
 #include <cstdint>
+#include <functional>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
 
@@ -140,12 +139,12 @@ class RpcLineItemDAOTest : public KuduTest {
 }; // class RpcLineItemDAOTest
 
 TEST_F(RpcLineItemDAOTest, TestInsert) {
-  dao_->WriteLine(boost::bind(BuildTestRow, 1, 1, _1));
+  dao_->WriteLine([](KuduPartialRow* row) { BuildTestRow(1, 1, row); });
   dao_->FinishWriting();
   ASSERT_EQ(1, CountRows());
   for (int i = 2; i < 10; i++) {
     for (int y = 0; y < 5; y++) {
-      dao_->WriteLine(boost::bind(BuildTestRow, i, y, _1));
+      dao_->WriteLine([=](KuduPartialRow* row) { BuildTestRow(i, y, row); });
     }
   }
   dao_->FinishWriting();
@@ -159,11 +158,11 @@ TEST_F(RpcLineItemDAOTest, TestInsert) {
 }
 
 TEST_F(RpcLineItemDAOTest, TestUpdate) {
-  dao_->WriteLine(boost::bind(BuildTestRow, 1, 1, _1));
+  dao_->WriteLine([](KuduPartialRow* row) { BuildTestRow(1, 1, row); });
   dao_->FinishWriting();
   ASSERT_EQ(1, CountRows());
 
-  dao_->MutateLine(boost::bind(UpdateTestRow, 1, 1, 12345, _1));
+  dao_->MutateLine([](KuduPartialRow* row) { UpdateTestRow(1, 1, 12345, row); });
   dao_->FinishWriting();
   unique_ptr<RpcLineItemDAO::Scanner> scanner;
   dao_->OpenScanner({ tpch::kQuantityColName }, &scanner);
diff --git a/src/kudu/benchmarks/tpch/rpc_line_item_dao.cc b/src/kudu/benchmarks/tpch/rpc_line_item_dao.cc
index fd047c4..cce71dd 100644
--- a/src/kudu/benchmarks/tpch/rpc_line_item_dao.cc
+++ b/src/kudu/benchmarks/tpch/rpc_line_item_dao.cc
@@ -17,12 +17,12 @@
 
 #include "kudu/benchmarks/tpch/rpc_line_item_dao.h"
 
+#include <functional>
 #include <memory>
 #include <ostream>
 #include <vector>
 #include <utility>
 
-#include <boost/function.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 
@@ -162,14 +162,14 @@ void RpcLineItemDAO::Init() {
                                   : KuduSession::AUTO_FLUSH_BACKGROUND));
 }
 
-void RpcLineItemDAO::WriteLine(const boost::function<void(KuduPartialRow*)> &f) {
+void RpcLineItemDAO::WriteLine(const std::function<void(KuduPartialRow*)>& f) {
   unique_ptr<KuduInsert> insert(client_table_->NewInsert());
   f(insert->mutable_row());
   CHECK_OK(session_->Apply(insert.release()));
   HandleLine();
 }
 
-void RpcLineItemDAO::MutateLine(const boost::function<void(KuduPartialRow*)> &f) {
+void RpcLineItemDAO::MutateLine(const std::function<void(KuduPartialRow*)>& f) {
   unique_ptr<KuduUpdate> update(client_table_->NewUpdate());
   f(update->mutable_row());
   CHECK_OK(session_->Apply(update.release()));
diff --git a/src/kudu/benchmarks/tpch/rpc_line_item_dao.h b/src/kudu/benchmarks/tpch/rpc_line_item_dao.h
index 0ad115b..29d50e2 100644
--- a/src/kudu/benchmarks/tpch/rpc_line_item_dao.h
+++ b/src/kudu/benchmarks/tpch/rpc_line_item_dao.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <functional>
 #include <memory>
 #include <string>
 #include <vector>
@@ -28,11 +29,6 @@
 #include "kudu/util/monotime.h"
 #include "kudu/util/semaphore.h"
 
-namespace boost {
-template <typename Signature>
-class function;
-} // namespace boost
-
 namespace kudu {
 
 namespace client {
@@ -59,8 +55,8 @@ class RpcLineItemDAO {
                  std::vector<const KuduPartialRow*> tablet_splits = {});
   ~RpcLineItemDAO();
   void Init();
-  void WriteLine(const boost::function<void(KuduPartialRow*)>& f);
-  void MutateLine(const boost::function<void(KuduPartialRow*)>& f);
+  void WriteLine(const std::function<void(KuduPartialRow*)>& f);
+  void MutateLine(const std::function<void(KuduPartialRow*)>& f);
   void FinishWriting();
 
   // Deletes previous scanner if one is open.
diff --git a/src/kudu/benchmarks/tpch/tpch1.cc b/src/kudu/benchmarks/tpch/tpch1.cc
index c4335f2..3471602 100644
--- a/src/kudu/benchmarks/tpch/tpch1.cc
+++ b/src/kudu/benchmarks/tpch/tpch1.cc
@@ -59,6 +59,7 @@
 
 #include <cstdint>
 #include <cstdlib>
+#include <functional>
 #include <memory>
 #include <ostream>
 #include <string>
@@ -66,8 +67,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 
@@ -86,6 +85,10 @@
 #include "kudu/util/status.h"
 #include "kudu/util/stopwatch.h"
 
+namespace kudu {
+class KuduPartialRow;
+}  // namespace kudu
+
 DEFINE_string(tpch_path_to_data, "/tmp/lineitem.tbl",
               "The full path to the '|' separated file containing the lineitem table.");
 DEFINE_int32(tpch_num_query_iterations, 1, "Number of times the query will be run.");
@@ -148,9 +151,9 @@ struct Hash {
 void LoadLineItems(const string &path, RpcLineItemDAO *dao) {
   LineItemTsvImporter importer(path);
 
+  auto f = [&importer](KuduPartialRow* row) { importer.GetNextLine(row); };
   while (importer.HasNextLine()) {
-    dao->WriteLine(boost::bind(&LineItemTsvImporter::GetNextLine,
-                               &importer, _1));
+    dao->WriteLine(f);
   }
   dao->FinishWriting();
 }
diff --git a/src/kudu/benchmarks/tpch/tpch_real_world.cc b/src/kudu/benchmarks/tpch/tpch_real_world.cc
index 0c49a3c..76d8326 100644
--- a/src/kudu/benchmarks/tpch/tpch_real_world.cc
+++ b/src/kudu/benchmarks/tpch/tpch_real_world.cc
@@ -46,6 +46,7 @@
 #include <csignal>
 #include <cstdint>
 #include <cstdlib>
+#include <functional>
 #include <iostream>
 #include <memory>
 #include <string>
@@ -53,8 +54,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 
@@ -311,8 +310,7 @@ void TpchRealWorld::LoadLineItemsThread(int i) {
   unique_ptr<RpcLineItemDAO> dao = GetInittedDAO();
   LineItemTsvImporter importer(GetNthLineItemFileName(i));
 
-  boost::function<void(KuduPartialRow*)> f =
-      boost::bind(&LineItemTsvImporter::GetNextLine, &importer, _1);
+  auto f = [&importer](KuduPartialRow* row) { importer.GetNextLine(row); };
   const string time_spent_msg = Substitute(
         "by thread $0 to load generated data into the database", i);
   LOG_TIMING(INFO, time_spent_msg) {
diff --git a/src/kudu/cfile/encoding-test.cc b/src/kudu/cfile/encoding-test.cc
index f7eea21..f2527bc 100644
--- a/src/kudu/cfile/encoding-test.cc
+++ b/src/kudu/cfile/encoding-test.cc
@@ -130,7 +130,7 @@ class TestEncoding : public KuduTest {
     // Insert "hello 0" through "hello 9"
     const uint kCount = 10;
     Slice s = CreateBinaryBlock(
-        &sbb, kCount, std::bind(StringPrintf, "hello %d", std::placeholders::_1));
+        &sbb, kCount, [](int item) { return StringPrintf("hello %d", item); });
     DecoderType sbd(s);
     ASSERT_OK(sbd.ParseHeader());
 
@@ -185,7 +185,7 @@ class TestEncoding : public KuduTest {
     const uint kCount = 1000;
     // Insert 'hello 000' through 'hello 999'
     Slice s = CreateBinaryBlock(
-        &sbb, kCount, std::bind(StringPrintf, "hello %03d", std::placeholders::_1));
+        &sbb, kCount, [](int item) { return StringPrintf("hello %03d", item); });
     BinaryPrefixBlockDecoder sbd(s);
     ASSERT_OK(sbd.ParseHeader());
 
@@ -476,7 +476,7 @@ class TestEncoding : public KuduTest {
     size_t sbsize;
 
     Slice s = CreateBinaryBlock(
-        &sbb, kCount, std::bind(StringPrintf, "hello %d", std::placeholders::_1));
+        &sbb, kCount, [](int item) { return StringPrintf("hello %d", item); });
     do {
       sbsize = s.size();
 
diff --git a/src/kudu/client/client-internal.cc b/src/kudu/client/client-internal.cc
index b8ba40f..9243836 100644
--- a/src/kudu/client/client-internal.cc
+++ b/src/kudu/client/client-internal.cc
@@ -30,10 +30,8 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
 #include <boost/container/small_vector.hpp>
 #include <boost/container/vector.hpp>
-#include <boost/function.hpp>
 #include <gflags/gflags_declare.h>
 #include <glog/logging.h>
 #include <google/protobuf/stubs/common.h>
@@ -92,6 +90,7 @@ class SignedTokenPB;
 
 using master::AlterTableRequestPB;
 using master::AlterTableResponsePB;
+using master::ConnectToMasterResponsePB;
 using master::CreateTableRequestPB;
 using master::CreateTableResponsePB;
 using master::DeleteTableRequestPB;
@@ -122,7 +121,7 @@ using internal::RemoteTabletServer;
 Status RetryFunc(const MonoTime& deadline,
                  const string& retry_msg,
                  const string& timeout_msg,
-                 const boost::function<Status(const MonoTime&, bool*)>& func) {
+                 const std::function<Status(const MonoTime&, bool*)>& func) {
   DCHECK(deadline.Initialized());
 
   if (deadline < MonoTime::Now()) {
@@ -183,10 +182,11 @@ KuduClient::Data::~Data() {
   dns_resolver_.reset();
 }
 
-RemoteTabletServer* KuduClient::Data::SelectTServer(const scoped_refptr<RemoteTablet>& rt,
-                                                    const ReplicaSelection selection,
-                                                    const set<string>& blacklist,
-                                                    vector<RemoteTabletServer*>* candidates) const {
+RemoteTabletServer* KuduClient::Data::SelectTServer(
+    const scoped_refptr<RemoteTablet>& rt,
+    const ReplicaSelection selection,
+    const set<string>& blacklist,
+    vector<RemoteTabletServer*>* candidates) const {
   RemoteTabletServer* ret = nullptr;
   candidates->clear();
   switch (selection) {
@@ -334,11 +334,13 @@ Status KuduClient::Data::WaitForCreateTableToFinish(
     KuduClient* client,
     TableIdentifierPB table,
     const MonoTime& deadline) {
-  return RetryFunc(deadline,
-                   "Waiting on Create Table to be completed",
-                   "Timed out waiting for Table Creation",
-                   boost::bind(&KuduClient::Data::IsCreateTableInProgress,
-                               this, client, std::move(table), _1, _2));
+  return RetryFunc(
+      deadline,
+      "Waiting on Create Table to be completed",
+      "Timed out waiting for Table Creation",
+      [&](const MonoTime& deadline, bool* retry) {
+        return IsCreateTableInProgress(client, table, deadline, retry);
+      });
 }
 
 Status KuduClient::Data::DeleteTable(KuduClient* client,
@@ -400,11 +402,13 @@ Status KuduClient::Data::WaitForAlterTableToFinish(
     KuduClient* client,
     TableIdentifierPB table,
     const MonoTime& deadline) {
-  return RetryFunc(deadline,
-                   "Waiting on Alter Table to be completed",
-                   "Timed out waiting for AlterTable",
-                   boost::bind(&KuduClient::Data::IsAlterTableInProgress,
-                               this, client, std::move(table), _1, _2));
+  return RetryFunc(
+      deadline,
+      "Waiting on Alter Table to be completed",
+      "Timed out waiting for AlterTable",
+      [&](const MonoTime& deadline, bool* retry) {
+        return IsAlterTableInProgress(client, table, deadline, retry);
+      });
 }
 
 Status KuduClient::Data::InitLocalHostNames() {
@@ -581,7 +585,7 @@ Status KuduClient::Data::GetTableSchema(KuduClient* client,
 void KuduClient::Data::ConnectedToClusterCb(
     const Status& status,
     const pair<Sockaddr, string>& leader_addr_and_name,
-    const master::ConnectToMasterResponsePB& connect_response,
+    const ConnectToMasterResponsePB& connect_response,
     CredentialsPolicy cred_policy) {
 
   const auto& leader_addr = leader_addr_and_name.first;
@@ -734,11 +738,11 @@ void KuduClient::Data::ConnectToClusterAsync(KuduClient* client,
     // policy.
     scoped_refptr<internal::ConnectToClusterRpc> rpc(
         new internal::ConnectToClusterRpc(
-            std::bind(&KuduClient::Data::ConnectedToClusterCb, this,
-            std::placeholders::_1,
-            std::placeholders::_2,
-            std::placeholders::_3,
-            creds_policy),
+            [this, creds_policy](const Status& status,
+                                 const std::pair<Sockaddr, string>& leader_master,
+                                 const ConnectToMasterResponsePB& connect_response) {
+              this->ConnectedToClusterCb(status, leader_master, connect_response, creds_policy);
+            },
         std::move(master_addrs_with_names),
         deadline,
         client->default_rpc_timeout(),
diff --git a/src/kudu/client/client-internal.h b/src/kudu/client/client-internal.h
index 9626ff6..bf5b3ce 100644
--- a/src/kudu/client/client-internal.h
+++ b/src/kudu/client/client-internal.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <functional>
 #include <map>
 #include <memory>
 #include <set>
@@ -38,11 +39,6 @@
 #include "kudu/util/status.h"
 #include "kudu/util/status_callback.h"
 
-namespace boost {
-template <typename Signature>
-class function;
-} // namespace boost
-
 namespace kudu {
 
 class DnsResolver;
@@ -101,40 +97,40 @@ class KuduClient::Data {
                          std::vector<internal::RemoteTabletServer*>* candidates,
                          internal::RemoteTabletServer** ts);
 
-  Status CreateTable(KuduClient* client,
-                     const master::CreateTableRequestPB& req,
-                     master::CreateTableResponsePB* resp,
-                     const MonoTime& deadline,
-                     bool has_range_partition_bounds);
-
-  Status IsCreateTableInProgress(KuduClient* client,
-                                 master::TableIdentifierPB table,
-                                 const MonoTime& deadline,
-                                 bool* create_in_progress);
-
-  Status WaitForCreateTableToFinish(KuduClient* client,
-                                    master::TableIdentifierPB table,
-                                    const MonoTime& deadline);
-
-  Status DeleteTable(KuduClient* client,
-                     const std::string& table_name,
-                     const MonoTime& deadline,
-                     bool modify_external_catalogs = true);
-
-  Status AlterTable(KuduClient* client,
-                    const master::AlterTableRequestPB& req,
-                    master::AlterTableResponsePB* resp,
-                    const MonoTime& deadline,
-                    bool has_add_drop_partition);
-
-  Status IsAlterTableInProgress(KuduClient* client,
-                                master::TableIdentifierPB table,
-                                const MonoTime& deadline,
-                                bool* alter_in_progress);
-
-  Status WaitForAlterTableToFinish(KuduClient* client,
-                                   master::TableIdentifierPB table,
-                                   const MonoTime& deadline);
+  static Status CreateTable(KuduClient* client,
+                            const master::CreateTableRequestPB& req,
+                            master::CreateTableResponsePB* resp,
+                            const MonoTime& deadline,
+                            bool has_range_partition_bounds);
+
+  static Status IsCreateTableInProgress(KuduClient* client,
+                                        master::TableIdentifierPB table,
+                                        const MonoTime& deadline,
+                                        bool* create_in_progress);
+
+  static Status WaitForCreateTableToFinish(KuduClient* client,
+                                           master::TableIdentifierPB table,
+                                           const MonoTime& deadline);
+
+  static Status DeleteTable(KuduClient* client,
+                            const std::string& table_name,
+                            const MonoTime& deadline,
+                            bool modify_external_catalogs = true);
+
+  static Status AlterTable(KuduClient* client,
+                           const master::AlterTableRequestPB& req,
+                           master::AlterTableResponsePB* resp,
+                           const MonoTime& deadline,
+                           bool has_add_drop_partition);
+
+  static Status IsAlterTableInProgress(KuduClient* client,
+                                       master::TableIdentifierPB table,
+                                       const MonoTime& deadline,
+                                       bool* alter_in_progress);
+
+  static Status WaitForAlterTableToFinish(KuduClient* client,
+                                          master::TableIdentifierPB table,
+                                          const MonoTime& deadline);
 
   // Open the table identified by 'table_identifier'.
   Status OpenTable(KuduClient* client,
@@ -325,7 +321,7 @@ class KuduClient::Data {
 Status RetryFunc(const MonoTime& deadline,
                  const std::string& retry_msg,
                  const std::string& timeout_msg,
-                 const boost::function<Status(const MonoTime&, bool*)>& func);
+                 const std::function<Status(const MonoTime&, bool*)>& func);
 
 // Set logging verbose level through environment variable.
 void SetVerboseLevelFromEnvVar();
diff --git a/src/kudu/client/client-test.cc b/src/kudu/client/client-test.cc
index 668c9d3..d500d6e 100644
--- a/src/kudu/client/client-test.cc
+++ b/src/kudu/client/client-test.cc
@@ -36,8 +36,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
 #include <boost/optional/optional.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
@@ -182,7 +180,6 @@ using kudu::security::SignedTokenPB;
 using kudu::client::sp::shared_ptr;
 using kudu::tablet::TabletReplica;
 using kudu::tserver::MiniTabletServer;
-using std::bind;
 using std::function;
 using std::map;
 using std::pair;
@@ -194,10 +191,6 @@ using std::unordered_set;
 using std::vector;
 using strings::Substitute;
 
-namespace boost {
-template <typename Signature> class function;
-}  // namespace boost
-
 namespace kudu {
 
 class RWMutex;
@@ -1462,7 +1455,7 @@ static void ReadBatchToStrings(KuduScanner* scanner, vector<string>* rows) {
 static void DoScanWithCallback(KuduTable* table,
                                const vector<string>& expected_rows,
                                int64_t limit,
-                               const boost::function<Status(const string&)>& cb) {
+                               const std::function<Status(const string&)>& cb) {
   // Initialize fault-tolerant snapshot scanner.
   KuduScanner scanner(table);
   if (limit > 0) {
@@ -1557,16 +1550,16 @@ TEST_F(ClientTest, TestScanFaultTolerance) {
 
       // Restarting and waiting should result in a SCANNER_EXPIRED error.
       LOG(INFO) << "Doing a scan while restarting a tserver and waiting for it to come up...";
-      NO_FATALS(internal::DoScanWithCallback(table.get(), expected_rows, limit,
-          boost::bind(&ClientTest_TestScanFaultTolerance_Test::RestartTServerAndWait,
-                      this, _1)));
+      NO_FATALS(internal::DoScanWithCallback(
+          table.get(), expected_rows, limit,
+          [this](const string& uuid) { return this->RestartTServerAndWait(uuid); }));
 
       // Restarting and not waiting means the tserver is hopefully bootstrapping, leading to
       // a TABLET_NOT_RUNNING error.
       LOG(INFO) << "Doing a scan while restarting a tserver...";
-      NO_FATALS(internal::DoScanWithCallback(table.get(), expected_rows, limit,
-          boost::bind(&ClientTest_TestScanFaultTolerance_Test::RestartTServerAsync,
-                      this, _1)));
+      NO_FATALS(internal::DoScanWithCallback(
+          table.get(), expected_rows, limit,
+          [this](const string& uuid) { return this->RestartTServerAsync(uuid); }));
       for (int i = 0; i < cluster_->num_tablet_servers(); i++) {
         MiniTabletServer* ts = cluster_->mini_tablet_server(i);
         ASSERT_OK(ts->WaitStarted());
@@ -1574,9 +1567,9 @@ TEST_F(ClientTest, TestScanFaultTolerance) {
 
       // Killing the tserver should lead to an RPC timeout.
       LOG(INFO) << "Doing a scan while killing a tserver...";
-      NO_FATALS(internal::DoScanWithCallback(table.get(), expected_rows, limit,
-          boost::bind(&ClientTest_TestScanFaultTolerance_Test::KillTServer,
-                      this, _1)));
+      NO_FATALS(internal::DoScanWithCallback(
+          table.get(), expected_rows, limit,
+          [this](const string& uuid) { return this->KillTServer(uuid); }));
 
       // Restart the server that we killed.
       for (int i = 0; i < cluster_->num_tablet_servers(); i++) {
@@ -3289,8 +3282,10 @@ TEST_F(ClientTest, TestSessionMutationBufferMaxNum) {
 
     size_t monitor_max_batchers_count = 0;
     CountDownLatch monitor_run_ctl(1);
-    thread monitor(bind(&ClientTest::MonitorSessionBatchersCount, session.get(),
-                        &monitor_run_ctl, &monitor_max_batchers_count));
+    thread monitor([&]() {
+      MonitorSessionBatchersCount(session.get(),
+                                  &monitor_run_ctl, &monitor_max_batchers_count);
+    });
 
     // Apply a big number of tiny operations, flushing after each to utilize
     // maximum possible number of session's batchers.
@@ -3503,8 +3498,10 @@ TEST_F(ClientTest, TestAutoFlushBackgroundSmallOps) {
 
   int64_t monitor_max_buffer_size = 0;
   CountDownLatch monitor_run_ctl(1);
-  thread monitor(bind(&ClientTest::MonitorSessionBufferSize, session.get(),
-                      &monitor_run_ctl, &monitor_max_buffer_size));
+  thread monitor([&]() {
+    MonitorSessionBufferSize(session.get(),
+                             &monitor_run_ctl, &monitor_max_buffer_size);
+  });
 
   for (size_t i = 0; i < kRowNum; ++i) {
     ASSERT_OK(ApplyInsertToSession(session.get(), client_table_, i, i, "x"));
@@ -3538,8 +3535,10 @@ TEST_F(ClientTest, TestAutoFlushBackgroundBigOps) {
 
   int64_t monitor_max_buffer_size = 0;
   CountDownLatch monitor_run_ctl(1);
-  thread monitor(bind(&ClientTest::MonitorSessionBufferSize, session.get(),
-                      &monitor_run_ctl, &monitor_max_buffer_size));
+  thread monitor([&]() {
+    MonitorSessionBufferSize(session.get(),
+                             &monitor_run_ctl, &monitor_max_buffer_size);
+  });
 
   // Starting from i == 3: this is the lowest i when
   // ((i - 1) * kBufferSizeBytes / i) has a value greater than
@@ -3579,8 +3578,10 @@ TEST_F(ClientTest, TestAutoFlushBackgroundRandomOps) {
   SeedRandom();
   int64_t monitor_max_buffer_size = 0;
   CountDownLatch monitor_run_ctl(1);
-  thread monitor(bind(&ClientTest::MonitorSessionBufferSize, session.get(),
-                      &monitor_run_ctl, &monitor_max_buffer_size));
+  thread monitor([&]() {
+    MonitorSessionBufferSize(session.get(),
+                             &monitor_run_ctl, &monitor_max_buffer_size);
+  });
 
   for (size_t i = 0; i < kRowNum; ++i) {
     // Every operation takes less than 2/3 of the buffer space, so no
diff --git a/src/kudu/client/client-unittest.cc b/src/kudu/client/client-unittest.cc
index 70b90ab..5c80386 100644
--- a/src/kudu/client/client-unittest.cc
+++ b/src/kudu/client/client-unittest.cc
@@ -22,11 +22,10 @@
 #include <openssl/crypto.h>
 
 #include <cstddef>
+#include <functional>
 #include <string>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
 #include <gtest/gtest.h>
 
 #include "kudu/client/client-internal.h"
@@ -198,7 +197,9 @@ TEST(ClientUnitTest, TestRetryFunc) {
   MonoTime deadline = MonoTime::Now() + MonoDelta::FromMilliseconds(100);
   int counter = 0;
   Status s = RetryFunc(deadline, "retrying test func", "timed out",
-                       boost::bind(TestFunc, _1, _2, &counter));
+                       [&](const MonoTime& deadline, bool* retry) {
+                         return TestFunc(deadline, retry, &counter);
+                       });
   ASSERT_TRUE(s.IsTimedOut());
   ASSERT_GT(counter, 5);
   ASSERT_LT(counter, 20);
diff --git a/src/kudu/client/client.cc b/src/kudu/client/client.cc
index 968537b..d4b607f 100644
--- a/src/kudu/client/client.cc
+++ b/src/kudu/client/client.cc
@@ -28,7 +28,6 @@
 #include <string>
 #include <vector>
 
-#include <boost/bind.hpp>
 #include <boost/optional/optional.hpp>
 #include <glog/logging.h>
 #include <google/protobuf/stubs/common.h>
@@ -69,7 +68,6 @@
 #include "kudu/common/wire_protocol.h"
 #include "kudu/common/wire_protocol.pb.h"
 #include "kudu/consensus/metadata.pb.h"
-#include "kudu/gutil/basictypes.h"
 #include "kudu/gutil/casts.h"
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/gutil/stl_util.h"
@@ -91,6 +89,7 @@
 #include "kudu/tserver/tserver.pb.h"
 #include "kudu/tserver/tserver_service.proxy.h"
 #include "kudu/util/async_util.h"
+#include "kudu/util/block_bloom_filter.h"
 #include "kudu/util/debug-util.h"
 #include "kudu/util/init.h"
 #include "kudu/util/logging.h"
@@ -156,8 +155,6 @@ struct tm;
 
 namespace kudu {
 
-class BlockBloomFilter;
-class BlockBloomFilterBufferAllocatorIf;
 class simple_spinlock;
 
 namespace client {
@@ -1684,9 +1681,10 @@ void KuduScanner::Close() {
     data_->PrepareRequest(KuduScanner::Data::CLOSE);
     data_->next_req_.set_close_scanner(true);
     closer->controller.set_timeout(data_->configuration().timeout());
-    data_->proxy_->ScanAsync(data_->next_req_, &closer->response, &closer->controller,
-                             boost::bind(&CloseCallback::Callback, closer.get()));
-    ignore_result(closer.release());
+    // CloseCallback::Callback() deletes the closer.
+    CloseCallback* closer_raw = closer.release();
+    data_->proxy_->ScanAsync(data_->next_req_, &closer_raw->response, &closer_raw->controller,
+                             [closer_raw]() { closer_raw->Callback(); });
   }
   data_->proxy_.reset();
   data_->open_ = false;
diff --git a/src/kudu/client/master_proxy_rpc.cc b/src/kudu/client/master_proxy_rpc.cc
index 45e78d1..815482d 100644
--- a/src/kudu/client/master_proxy_rpc.cc
+++ b/src/kudu/client/master_proxy_rpc.cc
@@ -127,8 +127,7 @@ void AsyncLeaderMasterRpc<ReqClass, RespClass>::SendRpc() {
     controller->RequireServerFeature(required_feature_flag);
   }
   func_(client_->data_->master_proxy().get(), *req_, resp_, controller,
-      std::bind(&AsyncLeaderMasterRpc<ReqClass, RespClass>::SendRpcCb,
-          this, Status::OK()));
+        [this]() { this->SendRpcCb(Status::OK()); });
 }
 
 template <class ReqClass, class RespClass>
diff --git a/src/kudu/client/master_rpc.cc b/src/kudu/client/master_rpc.cc
index b35bf9f..1bc99ac 100644
--- a/src/kudu/client/master_rpc.cc
+++ b/src/kudu/client/master_rpc.cc
@@ -20,12 +20,12 @@
 #include "kudu/client/master_rpc.h"
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <mutex>
 #include <ostream>
 #include <utility>
 
-#include <boost/bind.hpp>
 #include <glog/logging.h>
 
 #include "kudu/common/common.pb.h"
@@ -146,15 +146,11 @@ void ConnectToMasterRpc::SendRpc() {
     ConnectToMasterRequestPB req;
     controller->RequireServerFeature(master::MasterFeatures::CONNECT_TO_MASTER);
     proxy.ConnectToMasterAsync(req, out_, controller,
-                               boost::bind(&ConnectToMasterRpc::SendRpcCb,
-                                           this,
-                                           Status::OK()));
+                               [this]() { this->SendRpcCb(Status::OK()); });
   } else {
     GetMasterRegistrationRequestPB req;
     proxy.GetMasterRegistrationAsync(req, &old_rpc_resp_, controller,
-                                     boost::bind(&ConnectToMasterRpc::SendRpcCb,
-                                                 this,
-                                                 Status::OK()));
+                                     [this]() { this->SendRpcCb(Status::OK()); });
   }
 }
 
diff --git a/src/kudu/client/master_rpc.h b/src/kudu/client/master_rpc.h
index 83767fc..d888e4b 100644
--- a/src/kudu/client/master_rpc.h
+++ b/src/kudu/client/master_rpc.h
@@ -72,7 +72,7 @@ class ConnectToClusterRpc : public rpc::Rpc,
  public:
   typedef std::function<void(
       const Status& status,
-      const std::pair<Sockaddr, std::string> leader_master,
+      const std::pair<Sockaddr, std::string>& leader_master,
       const master::ConnectToMasterResponsePB& connect_response)> LeaderCallback;
   // The host and port of the leader master server is stored in
   // 'leader_master', which must remain valid for the lifetime of this
diff --git a/src/kudu/client/session-internal.cc b/src/kudu/client/session-internal.cc
index 9792f57..cc967e5 100644
--- a/src/kudu/client/session-internal.cc
+++ b/src/kudu/client/session-internal.cc
@@ -17,11 +17,10 @@
 
 #include "kudu/client/session-internal.h"
 
+#include <functional>
 #include <mutex>
 #include <utility>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
-#include <boost/function.hpp>
 #include <glog/logging.h>
 
 #include "kudu/client/batcher.h"
@@ -560,8 +559,10 @@ void KuduSession::Data::TimeBasedFlushTask(
   std::shared_ptr<rpc::Messenger> messenger(weak_messenger.lock());
   if (PREDICT_TRUE(messenger)) {
     messenger->ScheduleOnReactor(
-        boost::bind(&KuduSession::Data::TimeBasedFlushTask, _1,
-                    std::move(weak_messenger), std::move(weak_session), false),
+        [weak_messenger, weak_session](const Status& s) {
+          TimeBasedFlushTask(s, weak_messenger, weak_session,
+                             /*do_startup_check=*/ false);
+        },
         next_run);
   }
 }
diff --git a/src/kudu/consensus/consensus-test-util.h b/src/kudu/consensus/consensus-test-util.h
index dec636e..c0a7e72 100644
--- a/src/kudu/consensus/consensus-test-util.h
+++ b/src/kudu/consensus/consensus-test-util.h
@@ -25,7 +25,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
 #include <gmock/gmock.h>
 
 #include "kudu/clock/clock.h"
@@ -223,9 +222,9 @@ class DelayablePeerProxy : public TestPeerProxy {
                    rpc::RpcController* controller,
                    const rpc::ResponseCallback& callback) override {
     RegisterCallback(kUpdate, callback);
-    return proxy_->UpdateAsync(request, response, controller,
-                               boost::bind(&DelayablePeerProxy::RespondUnlessDelayed,
-                                           this, kUpdate));
+    return proxy_->UpdateAsync(
+        request, response, controller,
+        [this]() { this->RespondUnlessDelayed(kUpdate); });
   }
 
   void StartElectionAsync(const RunLeaderElectionRequestPB& /*request*/,
@@ -240,9 +239,9 @@ class DelayablePeerProxy : public TestPeerProxy {
                                  rpc::RpcController* controller,
                                  const rpc::ResponseCallback& callback) override {
     RegisterCallback(kRequestVote, callback);
-    return proxy_->RequestConsensusVoteAsync(request, response, controller,
-                                             boost::bind(&DelayablePeerProxy::RespondUnlessDelayed,
-                                                         this, kRequestVote));
+    return proxy_->RequestConsensusVoteAsync(
+        request, response, controller,
+        [this]() { this->RespondUnlessDelayed(kRequestVote); });
   }
 
   ProxyType* proxy() const {
@@ -705,11 +704,10 @@ class TestTransactionFactory : public ConsensusRoundHandler {
   }
 
   Status StartFollowerTransaction(const scoped_refptr<ConsensusRound>& round) override {
-    auto txn = new TestDriver(pool_.get(), log_, round);
-    txn->round_->SetConsensusReplicatedCallback(std::bind(
-        &TestDriver::ReplicationFinished,
-        txn,
-        std::placeholders::_1));
+    // 'txn' is deleted when it completes.
+    auto* txn = new TestDriver(pool_.get(), log_, round);
+    txn->round_->SetConsensusReplicatedCallback(
+        [txn](const Status& s) { txn->ReplicationFinished(s); });
     return Status::OK();
   }
 
diff --git a/src/kudu/consensus/leader_election-test.cc b/src/kudu/consensus/leader_election-test.cc
index 101ec13..657da21 100644
--- a/src/kudu/consensus/leader_election-test.cc
+++ b/src/kudu/consensus/leader_election-test.cc
@@ -233,9 +233,9 @@ scoped_refptr<LeaderElection> LeaderElectionTest::SetUpElectionWithHighTermVoter
       new LeaderElection(config_, proxy_factory_.get(),
                          std::move(request), std::move(counter),
                          MonoDelta::FromSeconds(kLeaderElectionTimeoutSecs),
-                         std::bind(&LeaderElectionTest::ElectionCallback,
-                                   this,
-                                   std::placeholders::_1)));
+                         [this](const ElectionResult& result) {
+                           this->ElectionCallback(result);
+                         }));
   return election;
 }
 
@@ -291,9 +291,9 @@ scoped_refptr<LeaderElection> LeaderElectionTest::SetUpElectionWithGrantDenyErro
       new LeaderElection(config_, proxy_factory_.get(),
                          std::move(request), std::move(counter),
                          MonoDelta::FromSeconds(kLeaderElectionTimeoutSecs),
-                         std::bind(&LeaderElectionTest::ElectionCallback,
-                                   this,
-                                   std::placeholders::_1)));
+                         [this](const ElectionResult& result) {
+                           this->ElectionCallback(result);
+                         }));
   return election;
 }
 
@@ -319,9 +319,9 @@ TEST_F(LeaderElectionTest, TestPerfectElection) {
         new LeaderElection(config_, proxy_factory_.get(),
                            std::move(request), std::move(counter),
                            MonoDelta::FromSeconds(kLeaderElectionTimeoutSecs),
-                           std::bind(&LeaderElectionTest::ElectionCallback,
-                                     this,
-                                     std::placeholders::_1)));
+                           [this](const ElectionResult& result) {
+                             this->ElectionCallback(result);
+                           }));
     election->Run();
     latch_.Wait();
 
@@ -449,9 +449,9 @@ TEST_F(LeaderElectionTest, TestFailToCreateProxy) {
       new LeaderElection(config_, proxy_factory_.get(),
                          std::move(request), std::move(counter),
                          MonoDelta::FromSeconds(kLeaderElectionTimeoutSecs),
-                         std::bind(&LeaderElectionTest::ElectionCallback,
-                                   this,
-                                   std::placeholders::_1)));
+                         [this](const ElectionResult& result) {
+                           this->ElectionCallback(result);
+                         }));
   election->Run();
   latch_.Wait();
   ASSERT_EQ(kElectionTerm, result_->vote_request.candidate_term());
diff --git a/src/kudu/consensus/raft_consensus.cc b/src/kudu/consensus/raft_consensus.cc
index 93699d6..eeccc35 100644
--- a/src/kudu/consensus/raft_consensus.cc
+++ b/src/kudu/consensus/raft_consensus.cc
@@ -527,15 +527,16 @@ Status RaftConsensus::StartElection(ElectionMode mode, ElectionReason reason) {
     *request.mutable_candidate_status()->mutable_last_received() =
         queue_->GetLastOpIdInLog();
 
+    auto self = shared_from_this();
     election.reset(new LeaderElection(
         std::move(active_config),
         // The RaftConsensus ref passed below ensures that this raw pointer
         // remains safe to use for the entirety of LeaderElection's life.
         peer_proxy_factory_.get(),
         std::move(request), std::move(counter), timeout,
-        std::bind(&RaftConsensus::ElectionCallback,
-                  shared_from_this(),
-                  reason, std::placeholders::_1)));
+        [self, reason](const ElectionResult& result) {
+          self->ElectionCallback(reason, result);
+        }));
   }
 
   // Start the election outside the lock.
@@ -704,12 +705,11 @@ Status RaftConsensus::BecomeLeaderUnlocked() {
 
   scoped_refptr<ConsensusRound> round(
       new ConsensusRound(this, make_scoped_refptr(new RefCountedReplicate(replicate))));
-  round->SetConsensusReplicatedCallback(std::bind(
-      &RaftConsensus::NonTxRoundReplicationFinished,
-      this,
-      round.get(),
-      &DoNothingStatusCB,
-      std::placeholders::_1));
+  auto* round_raw = round.get();
+  round->SetConsensusReplicatedCallback(
+      [this, round_raw](const Status& s) {
+        this->NonTxRoundReplicationFinished(round_raw, &DoNothingStatusCB, s);
+      });
 
   last_leader_communication_time_micros_ = 0;
 
@@ -2022,12 +2022,11 @@ Status RaftConsensus::BulkChangeConfig(const BulkChangeConfigRequestPB& req,
     new_config.clear_opid_index();
 
     RETURN_NOT_OK(ReplicateConfigChangeUnlocked(
-        committed_config, std::move(new_config), std::bind(
-            &RaftConsensus::MarkDirtyOnSuccess,
-            this,
-            string("Config change replication complete"),
-            std::move(client_cb),
-            std::placeholders::_1)));
+        committed_config, std::move(new_config),
+        [this, client_cb](const Status& s) {
+          this->MarkDirtyOnSuccess("Config change replication complete",
+                                   client_cb, s);
+        }));
   } // Release lock before signaling request.
   peer_manager_->SignalRequest();
   return Status::OK();
@@ -2247,18 +2246,16 @@ Status RaftConsensus::StartConsensusOnlyRoundUnlocked(const ReplicateRefPtr& msg
       << ": " << SecureShortDebugString(*msg->get());
   VLOG_WITH_PREFIX_UNLOCKED(1) << "Starting consensus round: "
                                << SecureShortDebugString(msg->get()->id());
+  auto client_cb = [this](const Status& s) {
+    this->MarkDirtyOnSuccess("Replicated consensus-only round",
+                             &DoNothingStatusCB, s);
+  };
   scoped_refptr<ConsensusRound> round(new ConsensusRound(this, msg));
-  StatusCallback client_cb = std::bind(&RaftConsensus::MarkDirtyOnSuccess,
-                                          this,
-                                          string("Replicated consensus-only round"),
-                                          &DoNothingStatusCB,
-                                          std::placeholders::_1);
-  round->SetConsensusReplicatedCallback(std::bind(
-      &RaftConsensus::NonTxRoundReplicationFinished,
-      this,
-      round.get(),
-      std::move(client_cb),
-      std::placeholders::_1));
+  auto* round_raw = round.get();
+  round->SetConsensusReplicatedCallback(
+      [this, round_raw, client_cb](const Status& s) {
+        this->NonTxRoundReplicationFinished(round_raw, client_cb, s);
+      });
   return AddPendingOperationUnlocked(round);
 }
 
@@ -2498,13 +2495,11 @@ Status RaftConsensus::ReplicateConfigChangeUnlocked(
 
   scoped_refptr<ConsensusRound> round(
       new ConsensusRound(this, make_scoped_refptr(new RefCountedReplicate(cc_replicate))));
-  round->SetConsensusReplicatedCallback(std::bind(
-      &RaftConsensus::NonTxRoundReplicationFinished,
-      this,
-      round.get(),
-      std::move(client_cb),
-      std::placeholders::_1));
-
+  auto* round_raw = round.get();
+  round->SetConsensusReplicatedCallback(
+      [this, round_raw, client_cb](const Status& s) {
+        this->NonTxRoundReplicationFinished(round_raw, client_cb, s);
+      });
   return AppendNewRoundToQueueUnlocked(round);
 }
 
diff --git a/src/kudu/integration-tests/linked_list-test-util.h b/src/kudu/integration-tests/linked_list-test-util.h
index a3d60f3..32bf71e 100644
--- a/src/kudu/integration-tests/linked_list-test-util.h
+++ b/src/kudu/integration-tests/linked_list-test-util.h
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <atomic>
+#include <functional>
 #include <iostream>
 #include <list>
 #include <memory>
@@ -26,8 +27,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
 #include <glog/logging.h>
 
 #include "kudu/client/client-test-util.h"
@@ -112,10 +111,10 @@ class LinkedListTester {
       int64_t *written_count);
 
   // Variant of VerifyLinkedListRemote that verifies at the specified snapshot timestamp.
-  Status VerifyLinkedListAtSnapshotRemote(const uint64_t snapshot_timestamp,
-                                          const int64_t expected,
-                                          const bool log_errors,
-                                          const boost::function<Status(const std::string&)>& cb,
+  Status VerifyLinkedListAtSnapshotRemote(uint64_t snapshot_timestamp,
+                                          int64_t expected,
+                                          bool log_errors,
+                                          const std::function<Status(const std::string&)>& cb,
                                           int64_t* verified_count) {
     return VerifyLinkedListRemote(snapshot_timestamp,
                                   expected,
@@ -124,9 +123,9 @@ class LinkedListTester {
                                   verified_count);
   }
 
-  Status VerifyLinkedListAtLatestRemote(const int64_t expected,
-                                        const bool log_errors,
-                                        const boost::function<Status(const std::string&)>& cb,
+  Status VerifyLinkedListAtLatestRemote(int64_t expected,
+                                        bool log_errors,
+                                        const std::function<Status(const std::string&)>& cb,
                                         int64_t* verified_count) {
     return VerifyLinkedListRemote(kNoSnapshot,
                                   expected,
@@ -136,9 +135,9 @@ class LinkedListTester {
   }
 
   // Variant of VerifyLinkedListremote that verifies at 'now'.
-  Status VerifyLinkedListAtNowSnapshotRemote(const int64_t expected,
-                                             const bool log_errors,
-                                             const boost::function<Status(const std::string&)>& cb,
+  Status VerifyLinkedListAtNowSnapshotRemote(int64_t expected,
+                                             bool log_errors,
+                                             const std::function<Status(const std::string&)>& cb,
                                              int64_t* verified_count) {
     return VerifyLinkedListRemote(kSnapshotAtNow,
                                   expected,
@@ -149,15 +148,15 @@ class LinkedListTester {
 
   // Run the verify step on a table with RPCs. Calls the provided callback 'cb' once during
   // verification to test scanner fault tolerance.
-  Status VerifyLinkedListRemote(const uint64_t snapshot_timestamp,
-                                const int64_t expected,
-                                const bool log_errors,
-                                const boost::function<Status(const std::string&)>& cb,
+  Status VerifyLinkedListRemote(uint64_t snapshot_timestamp,
+                                int64_t expected,
+                                bool log_errors,
+                                const std::function<Status(const std::string&)>& cb,
                                 int64_t* verified_count);
 
   // Run the verify step on a specific tablet.
   Status VerifyLinkedListLocal(const tablet::Tablet* tablet,
-                               const int64_t expected,
+                               int64_t expected,
                                int64_t* verified_count);
 
   // A variant of VerifyLinkedListRemote that is more robust towards ongoing
@@ -167,14 +166,14 @@ class LinkedListTester {
                        WaitAndVerifyMode mode = FINISH_WITH_SNAPSHOT_SCAN) {
     return WaitAndVerify(seconds_to_run,
                          expected,
-                         boost::bind(&LinkedListTester::ReturnOk, this, _1),
+                         [](const std::string& /*uuid*/) { return Status::OK(); },
                          mode);
   }
 
   // A variant of WaitAndVerify that also takes a callback to be run once during verification.
   Status WaitAndVerify(int seconds_to_run,
                        int64_t expected,
-                       const boost::function<Status(const std::string&)>& cb,
+                       const std::function<Status(const std::string&)>& cb,
                        WaitAndVerifyMode mode = FINISH_WITH_SNAPSHOT_SCAN);
 
   // Generates a vector of keys for the table such that each tablet is
@@ -197,9 +196,6 @@ class LinkedListTester {
   HdrHistogram latency_histogram_;
   client::sp::shared_ptr<client::KuduClient> client_;
   SnapsAndCounts sampled_timestamps_and_counts_;
-
- private:
-  Status ReturnOk(const std::string& str) { return Status::OK(); }
 };
 
 // Generates the linked list pattern.
@@ -338,7 +334,7 @@ class LinkedListVerifier {
 // LinkedListTester
 /////////////////////////////////////////////////////////////
 
-std::vector<const KuduPartialRow*> LinkedListTester::GenerateSplitRows(
+inline std::vector<const KuduPartialRow*> LinkedListTester::GenerateSplitRows(
     const client::KuduSchema& schema) {
   std::vector<const KuduPartialRow*> split_keys;
   for (int64_t val : GenerateSplitInts()) {
@@ -349,7 +345,7 @@ std::vector<const KuduPartialRow*> LinkedListTester::GenerateSplitRows(
   return split_keys;
 }
 
-std::vector<int64_t> LinkedListTester::GenerateSplitInts() {
+inline std::vector<int64_t> LinkedListTester::GenerateSplitInts() {
   std::vector<int64_t> ret;
   ret.reserve(num_tablets_ - 1);
   const int64_t increment = kint64max / num_tablets_;
@@ -359,7 +355,7 @@ std::vector<int64_t> LinkedListTester::GenerateSplitInts() {
   return ret;
 }
 
-Status LinkedListTester::CreateLinkedListTable() {
+inline Status LinkedListTester::CreateLinkedListTable() {
   std::unique_ptr<client::KuduTableCreator> table_creator(
       client_->NewTableCreator());
 #pragma GCC diagnostic push
@@ -375,7 +371,7 @@ Status LinkedListTester::CreateLinkedListTable() {
   return Status::OK();
 }
 
-Status LinkedListTester::LoadLinkedList(
+inline Status LinkedListTester::LoadLinkedList(
     const MonoDelta& run_for,
     int num_samples,
     int64_t *written_count) {
@@ -450,7 +446,7 @@ Status LinkedListTester::LoadLinkedList(
   }
 }
 
-void LinkedListTester::DumpInsertHistogram(bool print_flags) {
+inline void LinkedListTester::DumpInsertHistogram(bool print_flags) {
   // We dump to cout instead of using glog so the output isn't prefixed with
   // line numbers. This makes it less ugly to copy-paste into JIRA, etc.
   using std::cout;
@@ -481,9 +477,9 @@ static void VerifyNoDuplicateEntries(const std::vector<int64_t>& ints, int* erro
   }
 }
 
-Status LinkedListTester::VerifyLinkedListRemote(
-    const uint64_t snapshot_timestamp, const int64_t expected, bool log_errors,
-    const boost::function<Status(const std::string&)>& cb, int64_t* verified_count) {
+inline Status LinkedListTester::VerifyLinkedListRemote(
+    uint64_t snapshot_timestamp, int64_t expected, bool log_errors,
+    const std::function<Status(const std::string&)>& cb, int64_t* verified_count) {
 
   client::sp::shared_ptr<client::KuduTable> table;
   RETURN_NOT_OK(client_->OpenTable(table_name_, &table));
@@ -561,10 +557,9 @@ Status LinkedListTester::VerifyLinkedListRemote(
   return s;
 }
 
-Status LinkedListTester::VerifyLinkedListLocal(const tablet::Tablet* tablet,
-                                               int64_t expected,
-                                               int64_t* verified_count) {
-  DCHECK(tablet != NULL);
+inline Status LinkedListTester::VerifyLinkedListLocal(
+    const tablet::Tablet* tablet, int64_t expected, int64_t* verified_count) {
+  DCHECK(tablet);
   LinkedListVerifier verifier(num_chains_, enable_mutation_, expected,
                               GenerateSplitInts());
   verifier.StartScanTimer();
@@ -598,10 +593,9 @@ Status LinkedListTester::VerifyLinkedListLocal(const tablet::Tablet* tablet,
   return verifier.VerifyData(verified_count, true);
 }
 
-Status LinkedListTester::WaitAndVerify(int seconds_to_run,
-                                       int64_t expected,
-                                       const boost::function<Status(const std::string&)>& cb,
-                                       WaitAndVerifyMode mode) {
+inline Status LinkedListTester::WaitAndVerify(
+    int seconds_to_run, int64_t expected,
+    const std::function<Status(const std::string&)>& cb, WaitAndVerifyMode mode) {
 
   std::list<std::pair<int64_t, int64_t>> samples_as_list(
       sampled_timestamps_and_counts_.begin(),
@@ -629,9 +623,10 @@ Status LinkedListTester::WaitAndVerify(int seconds_to_run,
                                              &seen);
         called = true;
       } else {
-        s = VerifyLinkedListAtSnapshotRemote((*iter).first, (*iter).second, last_attempt,
-                                             boost::bind(&LinkedListTester::ReturnOk, this, _1),
-                                             &seen);
+        s = VerifyLinkedListAtSnapshotRemote(
+            (*iter).first, (*iter).second, last_attempt,
+            [](const std::string& /*uuid*/) { return Status::OK(); },
+            &seen);
       }
 
       if (s.ok() && (*iter).second != seen) {
@@ -662,7 +657,8 @@ Status LinkedListTester::WaitAndVerify(int seconds_to_run,
       case FINISH_WITH_SNAPSHOT_SCAN:
         if (s.ok()) {
           RETURN_NOT_OK(VerifyLinkedListAtNowSnapshotRemote(
-              expected, last_attempt, boost::bind(&LinkedListTester::ReturnOk, this, _1), &seen));
+              expected, last_attempt,
+              [](const std::string& /*uuid*/) { return Status::OK(); }, &seen));
         }
         break;
       case FINISH_WITH_SCAN_LATEST:
@@ -670,7 +666,8 @@ Status LinkedListTester::WaitAndVerify(int seconds_to_run,
         // so, in this case, retry.
         if (s.ok()) {
           s = VerifyLinkedListAtLatestRemote(
-              expected, last_attempt, boost::bind(&LinkedListTester::ReturnOk, this, _1), &seen);
+              expected, last_attempt,
+              [](const std::string& /*uuid*/) { return Status::OK(); }, &seen);
         }
         break;
     }
diff --git a/src/kudu/integration-tests/linked_list-test.cc b/src/kudu/integration-tests/linked_list-test.cc
index 5d124cd..7571dfa 100644
--- a/src/kudu/integration-tests/linked_list-test.cc
+++ b/src/kudu/integration-tests/linked_list-test.cc
@@ -31,6 +31,7 @@
 // either zero or one times, and no link_to refers to a missing key.
 
 #include <cstdint>
+#include <functional>
 #include <memory>
 #include <ostream>
 #include <string>
@@ -38,7 +39,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
@@ -198,10 +198,9 @@ TEST_F(LinkedListTest, TestLoadAndVerify) {
     // Restart a tserver during a scan to test scanner fault tolerance.
     WaitForTSAndReplicas();
     LOG(INFO) << "Will restart the tablet server during verification scan.";
-    ASSERT_OK(tester_->WaitAndVerify(FLAGS_seconds_to_run, written,
-                                     boost::bind(
-                                         &TabletServerIntegrationTestBase::RestartServerWithUUID,
-                                         this, _1)));
+    ASSERT_OK(tester_->WaitAndVerify(
+        FLAGS_seconds_to_run, written,
+        [this](const string& uuid) { return this->RestartServerWithUUID(uuid); }));
     LOG(INFO) << "Done with tserver restart test.";
     ASSERT_OK(CheckTabletServersAreAlive(tablet_servers_.size()));
 
@@ -209,10 +208,9 @@ TEST_F(LinkedListTest, TestLoadAndVerify) {
     // Note that the previously restarted node is likely still be bootstrapping, which makes this
     // even harder.
     LOG(INFO) << "Will kill the tablet server during verification scan.";
-    ASSERT_OK(tester_->WaitAndVerify(FLAGS_seconds_to_run, written,
-                                     boost::bind(
-                                         &TabletServerIntegrationTestBase::ShutdownServerWithUUID,
-                                         this, _1)));
+    ASSERT_OK(tester_->WaitAndVerify(
+        FLAGS_seconds_to_run, written,
+        [this](const string& uuid) { return this->ShutdownServerWithUUID(uuid); }));
     LOG(INFO) << "Done with tserver kill test.";
     ASSERT_OK(CheckTabletServersAreAlive(tablet_servers_.size()-1));
     NO_FATALS(RestartCluster());
diff --git a/src/kudu/integration-tests/tablet_replacement-itest.cc b/src/kudu/integration-tests/tablet_replacement-itest.cc
index fa92085..096fa78 100644
--- a/src/kudu/integration-tests/tablet_replacement-itest.cc
+++ b/src/kudu/integration-tests/tablet_replacement-itest.cc
@@ -27,7 +27,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
 #include <gflags/gflags_declare.h>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
@@ -652,7 +651,7 @@ TEST_F(TabletReplacementITest, TestRemoteBoostrapWithPendingConfigChangeCommits)
   ASSERT_OK(SchemaToPB(schema, req.mutable_schema()));
   AddTestRowToPB(RowOperationsPB::INSERT, schema, 1, 1, "", req.mutable_row_operations());
   leader_ts->tserver_proxy->WriteAsync(req, &resp, &rpc,
-                                       boost::bind(&CountDownLatch::CountDown, &latch));
+                                       [&latch]() { latch.CountDown(); });
 
   // Wait for the replicate to show up (this doesn't wait for COMMIT messages).
   ASSERT_OK(WaitForServersToAgree(timeout, ts_map_, tablet_id, 3));
diff --git a/src/kudu/integration-tests/token_signer-itest.cc b/src/kudu/integration-tests/token_signer-itest.cc
index d02c758..32d1626 100644
--- a/src/kudu/integration-tests/token_signer-itest.cc
+++ b/src/kudu/integration-tests/token_signer-itest.cc
@@ -214,11 +214,6 @@ TEST_F(TokenSignerITest, TskMasterLeadershipChange) {
 //   * Make sure the TSK stays valid and can be used for token verification
 //     up to the very end of the token validity interval.
 TEST_F(TokenSignerITest, AuthnTokenLifecycle) {
-  using std::all_of;
-  using std::bind;
-  using std::equal_to;
-  using std::placeholders::_1;
-
   if (!AllowSlowTests()) {
     LOG(WARNING) << "test is skipped; set KUDU_ALLOW_SLOW_TESTS=1 to run";
     return;
@@ -300,8 +295,8 @@ TEST_F(TokenSignerITest, AuthnTokenLifecycle) {
         ASSERT_LT(token.expire_unix_epoch_seconds(), time_post);
       }
     }
-    if (all_of(expired_at_tserver.begin(), expired_at_tserver.end(),
-               bind(equal_to<bool>(), _1, true))) {
+    if (std::all_of(expired_at_tserver.begin(), expired_at_tserver.end(),
+                    [](bool i) { return i; })) {
       break;
     }
     SleepFor(MonoDelta::FromMilliseconds(500));
@@ -309,8 +304,8 @@ TEST_F(TokenSignerITest, AuthnTokenLifecycle) {
 
   // The end-of-TSK-activity-interval authn token should have been successfully
   // validated by all tablet servers.
-  ASSERT_TRUE(all_of(valid_at_tserver.begin(), valid_at_tserver.end(),
-              bind(equal_to<bool>(), _1, true)));
+  ASSERT_TRUE(std::all_of(valid_at_tserver.begin(), valid_at_tserver.end(),
+                          [](bool i) { return i; }));
 
   while (WallTime_Now() < key_expire) {
     SleepFor(MonoDelta::FromMilliseconds(500));
diff --git a/src/kudu/master/catalog_manager.cc b/src/kudu/master/catalog_manager.cc
index 4ddf84c..415b21a 100644
--- a/src/kudu/master/catalog_manager.cc
+++ b/src/kudu/master/catalog_manager.cc
@@ -59,8 +59,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
 #include <boost/optional/optional.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
@@ -1147,7 +1145,7 @@ void CatalogManager::PrepareForLeadershipTask() {
         "Loading table and tablet metadata into memory";
     LOG(INFO) << kLoadMetaOpDescription << "...";
     LOG_SLOW_EXECUTION(WARNING, 1000, LogPrefix() + kLoadMetaOpDescription) {
-      if (!check(std::bind(&CatalogManager::VisitTablesAndTabletsUnlocked, this),
+      if (!check([this]() { return this->VisitTablesAndTabletsUnlocked(); },
                  *consensus, term, kLoadMetaOpDescription).ok()) {
         return;
       }
@@ -1158,7 +1156,7 @@ void CatalogManager::PrepareForLeadershipTask() {
         "Initializing Kudu internal certificate authority";
     LOG(INFO) << kCaInitOpDescription << "...";
     LOG_SLOW_EXECUTION(WARNING, 1000, LogPrefix() + kCaInitOpDescription) {
-      if (!check(std::bind(&CatalogManager::InitCertAuthority, this),
+      if (!check([this]() { return this->InitCertAuthority(); },
                  *consensus, term, kCaInitOpDescription).ok()) {
         return;
       }
@@ -1167,7 +1165,7 @@ void CatalogManager::PrepareForLeadershipTask() {
     static const char* const kTskOpDescription = "Loading token signing keys";
     LOG(INFO) << kTskOpDescription << "...";
     LOG_SLOW_EXECUTION(WARNING, 1000, LogPrefix() + kTskOpDescription) {
-      if (!check(std::bind(&CatalogManager::InitTokenSigner, this),
+      if (!check([this]() { return this->InitTokenSigner(); },
                  *consensus, term, kTskOpDescription).ok()) {
         return;
       }
@@ -1177,9 +1175,10 @@ void CatalogManager::PrepareForLeadershipTask() {
         "Initializing in-progress tserver states";
     LOG(INFO) << kTServerStatesDescription << "...";
     LOG_SLOW_EXECUTION(WARNING, 1000, LogPrefix() + kTServerStatesDescription) {
-      if (!check(std::bind(&TSManager::ReloadTServerStates, master_->ts_manager(),
-                           sys_catalog_.get()),
-                 *consensus, term, kTServerStatesDescription).ok()) {
+      if (!check([this]() {
+            return this->master_->ts_manager()->ReloadTServerStates(this->sys_catalog_.get());
+          },
+          *consensus, term, kTServerStatesDescription).ok()) {
         return;
       }
     }
@@ -1189,8 +1188,8 @@ void CatalogManager::PrepareForLeadershipTask() {
           "Loading latest processed Hive Metastore notification log event ID";
       LOG(INFO) << kNotificationLogEventIdDescription << "...";
       LOG_SLOW_EXECUTION(WARNING, 1000, LogPrefix() + kNotificationLogEventIdDescription) {
-        if (!check(std::bind(&CatalogManager::InitLatestNotificationLogEventId, this),
-                   *consensus, term, kNotificationLogEventIdDescription).ok()) {
+      if (!check([this]() { return this->InitLatestNotificationLogEventId(); },
+                 *consensus, term, kNotificationLogEventIdDescription).ok()) {
           return;
         }
       }
@@ -3373,7 +3372,7 @@ bool RetryingTSRpcTask::RescheduleWithBackoffDelay() {
   VLOG(1) << Substitute("Scheduling retry of $0 with a delay of $1 ms (attempt = $2)",
                         description(), delay_millis, attempt_);
   master_->messenger()->ScheduleOnReactor(
-      boost::bind(&RetryingTSRpcTask::RunDelayedTask, this, _1),
+      [this](const Status& s) { this->RunDelayedTask(s); },
       MonoDelta::FromMilliseconds(delay_millis));
   return true;
 }
@@ -3512,7 +3511,7 @@ class AsyncCreateReplica : public RetrySpecificTSRpcTask {
                           type_name(), target_ts_desc_->ToString(), attempt,
                           SecureDebugString(req_));
     ts_proxy_->CreateTabletAsync(req_, &resp_, &rpc_,
-                                 boost::bind(&AsyncCreateReplica::RpcCallback, this));
+                                 [this]() { this->RpcCallback(); });
     return true;
   }
 
@@ -3610,7 +3609,7 @@ class AsyncDeleteReplica : public RetrySpecificTSRpcTask {
                           type_name(), target_ts_desc_->ToString(), attempt,
                           SecureDebugString(req));
     ts_proxy_->DeleteTabletAsync(req, &resp_, &rpc_,
-                                 boost::bind(&AsyncDeleteReplica::RpcCallback, this));
+                                 [this]() { this->RpcCallback(); });
     return true;
   }
 
@@ -3697,7 +3696,7 @@ class AsyncAlterTable : public RetryingTSRpcTask {
                           type_name(), target_ts_desc_->ToString(), attempt,
                           SecureDebugString(req));
     ts_proxy_->AlterSchemaAsync(req, &resp_, &rpc_,
-                                boost::bind(&AsyncAlterTable::RpcCallback, this));
+                                [this]() { this->RpcCallback(); });
     return true;
   }
 
@@ -3927,7 +3926,7 @@ bool AsyncAddReplicaTask::SendRequest(int attempt) {
                         type_name(), target_ts_desc_->ToString(), attempt,
                         SecureDebugString(req));
   consensus_proxy_->ChangeConfigAsync(req, &resp_, &rpc_,
-                                      boost::bind(&AsyncAddReplicaTask::RpcCallback, this));
+                                      [this]() { this->RpcCallback(); });
   return true;
 }
 
@@ -3978,7 +3977,7 @@ bool AsyncEvictReplicaTask::SendRequest(int attempt) {
                         type_name(), target_ts_desc_->ToString(), attempt,
                         SecureDebugString(req));
   consensus_proxy_->ChangeConfigAsync(req, &resp_, &rpc_,
-                                      boost::bind(&AsyncEvictReplicaTask::RpcCallback, this));
+                                      [this]() { this->RpcCallback(); });
   return true;
 }
 
diff --git a/src/kudu/master/master-test.cc b/src/kudu/master/master-test.cc
index 52b8037..fc6c101 100644
--- a/src/kudu/master/master-test.cc
+++ b/src/kudu/master/master-test.cc
@@ -33,7 +33,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
 #include <gflags/gflags_declare.h>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
@@ -935,7 +934,7 @@ TEST_F(MasterTest, TestDumpStacksOnRpcQueueOverflow) {
   CountDownLatch latch(kNumRpcs);
   for (int i = 0; i < kNumRpcs; i++) {
     proxy_->GetTableLocationsAsync(req, &resps[i], &rpcs[i],
-                                   boost::bind(&CountDownLatch::CountDown, &latch));
+                                   [&latch]() { latch.CountDown(); });
   }
   latch.Wait();
 
diff --git a/src/kudu/master/master_path_handlers.cc b/src/kudu/master/master_path_handlers.cc
index 2e6837a..d1c57ae 100644
--- a/src/kudu/master/master_path_handlers.cc
+++ b/src/kudu/master/master_path_handlers.cc
@@ -20,6 +20,7 @@
 #include <algorithm>
 #include <array>
 #include <cstdint>
+#include <functional>
 #include <limits>
 #include <map>
 #include <memory>
@@ -30,7 +31,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
 #include <boost/optional/optional.hpp>
 #include <glog/logging.h>
 
@@ -780,23 +780,33 @@ Status MasterPathHandlers::Register(Webserver* server) {
   bool is_on_nav_bar = true;
   server->RegisterPathHandler(
       "/tablet-servers", "Tablet Servers",
-      boost::bind(&MasterPathHandlers::HandleTabletServers, this, _1, _2),
+      [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+        this->HandleTabletServers(req, resp);
+      },
       is_styled, is_on_nav_bar);
   server->RegisterPathHandler(
       "/tables", "Tables",
-      boost::bind(&MasterPathHandlers::HandleCatalogManager, this, _1, _2),
+      [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+        this->HandleCatalogManager(req, resp);
+      },
       is_styled, is_on_nav_bar);
   server->RegisterPathHandler(
       "/table", "",
-      boost::bind(&MasterPathHandlers::HandleTablePage, this, _1, _2),
+      [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+        this->HandleTablePage(req, resp);
+      },
       is_styled, false);
   server->RegisterPathHandler(
       "/masters", "Masters",
-      boost::bind(&MasterPathHandlers::HandleMasters, this, _1, _2),
+      [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+        this->HandleMasters(req, resp);
+      },
       is_styled, is_on_nav_bar);
   server->RegisterPrerenderedPathHandler(
       "/dump-entities", "Dump Entities",
-      boost::bind(&MasterPathHandlers::HandleDumpEntities, this, _1, _2),
+      [this](const Webserver::WebRequest& req, Webserver::PrerenderedWebResponse* resp) {
+        this->HandleDumpEntities(req, resp);
+      },
       false, false);
   return Status::OK();
 }
diff --git a/src/kudu/rpc/messenger.cc b/src/kudu/rpc/messenger.cc
index 48af449..5df8fb5 100644
--- a/src/kudu/rpc/messenger.cc
+++ b/src/kudu/rpc/messenger.cc
@@ -59,10 +59,6 @@ using std::make_shared;
 using std::unique_ptr;
 using strings::Substitute;
 
-namespace boost {
-template <typename Signature> class function;
-}
-
 namespace kudu {
 namespace rpc {
 
@@ -458,7 +454,7 @@ Status Messenger::DumpConnections(const DumpConnectionsRequestPB& req,
   return Status::OK();
 }
 
-void Messenger::ScheduleOnReactor(const boost::function<void(const Status&)>& func,
+void Messenger::ScheduleOnReactor(std::function<void(const Status&)> func,
                                   MonoDelta when) {
   DCHECK(!reactors_.empty());
 
@@ -474,7 +470,7 @@ void Messenger::ScheduleOnReactor(const boost::function<void(const Status&)>& fu
     chosen = reactors_[rand() % reactors_.size()];
   }
 
-  DelayedTask* task = new DelayedTask(func, when);
+  DelayedTask* task = new DelayedTask(std::move(func), when);
   chosen->ScheduleReactorTask(task);
 }
 
diff --git a/src/kudu/rpc/messenger.h b/src/kudu/rpc/messenger.h
index 5a5f885..2cb1ecd 100644
--- a/src/kudu/rpc/messenger.h
+++ b/src/kudu/rpc/messenger.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <functional>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -38,11 +39,6 @@
 #include "kudu/util/net/sockaddr.h"
 #include "kudu/util/status.h"
 
-namespace boost {
-template <typename Signature>
-class function;
-}
-
 namespace kudu {
 
 class Socket;
@@ -271,7 +267,7 @@ class Messenger {
   //
   // The status argument conveys whether 'func' was run correctly (i.e.
   // after the elapsed time) or not.
-  void ScheduleOnReactor(const boost::function<void(const Status&)>& func,
+  void ScheduleOnReactor(std::function<void(const Status&)> func,
                          MonoDelta when);
 
   const security::TlsContext& tls_context() const { return *tls_context_; }
diff --git a/src/kudu/rpc/negotiation-test.cc b/src/kudu/rpc/negotiation-test.cc
index 50e226b..90044ac 100644
--- a/src/kudu/rpc/negotiation-test.cc
+++ b/src/kudu/rpc/negotiation-test.cc
@@ -1026,13 +1026,11 @@ static void RunNegotiationTest(const SocketCallable& server_runner,
 ////////////////////////////////////////////////////////////////////////////////
 
 #ifndef __APPLE__
-template<class T>
-using CheckerFunction = std::function<void(const Status&, T&)>;
 
 // Run GSSAPI negotiation from the server side. Runs
 // 'post_check' after negotiation to verify the result.
 static void RunGSSAPINegotiationServer(unique_ptr<Socket> socket,
-                                       const CheckerFunction<ServerNegotiation>& post_check) {
+                                       const std::function<void(const Status&)>& post_check) {
   TlsContext tls_context;
   CHECK_OK(tls_context.Init());
   TokenVerifier token_verifier;
@@ -1040,20 +1038,20 @@ static void RunGSSAPINegotiationServer(unique_ptr<Socket> socket,
                                        &token_verifier, RpcEncryption::OPTIONAL, "kudu");
   server_negotiation.set_server_fqdn("127.0.0.1");
   CHECK_OK(server_negotiation.EnableGSSAPI());
-  post_check(server_negotiation.Negotiate(), server_negotiation);
+  post_check(server_negotiation.Negotiate());
 }
 
 // Run GSSAPI negotiation from the client side. Runs
 // 'post_check' after negotiation to verify the result.
 static void RunGSSAPINegotiationClient(unique_ptr<Socket> conn,
-                                       const CheckerFunction<ClientNegotiation>& post_check) {
+                                       const std::function<void(const Status&)>& post_check) {
   TlsContext tls_context;
   CHECK_OK(tls_context.Init());
   ClientNegotiation client_negotiation(std::move(conn), &tls_context,
                                        boost::none, RpcEncryption::OPTIONAL, "kudu");
   client_negotiation.set_server_fqdn("127.0.0.1");
   CHECK_OK(client_negotiation.EnableGSSAPI());
-  post_check(client_negotiation.Negotiate(), client_negotiation);
+  post_check(client_negotiation.Negotiate());
 }
 
 // Test invalid SASL negotiations using the GSSAPI (kerberos) mechanism over a socket.
@@ -1067,23 +1065,29 @@ TEST_F(TestNegotiation, TestGSSAPIInvalidNegotiation) {
   // Try to negotiate with no krb5 credentials on either side. It should fail on both
   // sides.
   RunNegotiationTest(
-      std::bind(RunGSSAPINegotiationServer, std::placeholders::_1,
-                [](const Status& s, ServerNegotiation& server) {
-                  // The client notices there are no credentials and
-                  // doesn't send any failure message to the server.
-                  // Instead, it just disconnects.
-                  //
-                  // TODO(todd): it might be preferable to have the server
-                  // fail to start if it has no valid keytab.
-                  CHECK(s.IsNetworkError());
-                }),
-      std::bind(RunGSSAPINegotiationClient, std::placeholders::_1,
-                [](const Status& s, ClientNegotiation& client) {
-                  CHECK(s.IsNotAuthorized());
+      [](unique_ptr<Socket> socket) {
+        RunGSSAPINegotiationServer(
+            std::move(socket),
+            [](const Status& s) {
+              // The client notices there are no credentials and
+              // doesn't send any failure message to the server.
+              // Instead, it just disconnects.
+              //
+              // TODO(todd): it might be preferable to have the server
+              // fail to start if it has no valid keytab.
+              CHECK(s.IsNetworkError());
+            });
+      },
+      [](unique_ptr<Socket> socket) {
+        RunGSSAPINegotiationClient(
+            std::move(socket),
+            [](const Status& s) {
+              CHECK(s.IsNotAuthorized());
 #ifndef KRB5_VERSION_LE_1_10
-                  CHECK_GT(s.ToString().find("No Kerberos credentials available"), 0);
+              CHECK_GT(s.ToString().find("No Kerberos credentials available"), 0);
 #endif
-                }));
+            });
+      });
 
   // Create the server principal and keytab.
   string kt_path;
@@ -1093,20 +1097,26 @@ TEST_F(TestNegotiation, TestGSSAPIInvalidNegotiation) {
   // Try to negotiate with no krb5 credentials on the client. It should fail on both
   // sides.
   RunNegotiationTest(
-      std::bind(RunGSSAPINegotiationServer, std::placeholders::_1,
-                [](const Status& s, ServerNegotiation& server) {
-                  // The client notices there are no credentials and
-                  // doesn't send any failure message to the server.
-                  // Instead, it just disconnects.
-                  CHECK(s.IsNetworkError());
-                }),
-      std::bind(RunGSSAPINegotiationClient, std::placeholders::_1,
-                [](const Status& s, ClientNegotiation& client) {
-                  CHECK(s.IsNotAuthorized());
-                  ASSERT_STR_MATCHES(s.ToString(),
-                                     "Not authorized: server requires authentication, "
-                                     "but client does not have Kerberos credentials available");
-                }));
+      [](unique_ptr<Socket> socket) {
+        RunGSSAPINegotiationServer(
+            std::move(socket),
+            [](const Status& s) {
+              // The client notices there are no credentials and
+              // doesn't send any failure message to the server.
+              // Instead, it just disconnects.
+              CHECK(s.IsNetworkError());
+            });
+      },
+      [](unique_ptr<Socket> socket) {
+        RunGSSAPINegotiationClient(
+            std::move(socket),
+            [](const Status& s) {
+              CHECK(s.IsNotAuthorized());
+              ASSERT_STR_MATCHES(s.ToString(),
+                                 "Not authorized: server requires authentication, "
+                                 "but client does not have Kerberos credentials available");
+            });
+      });
 
   // Create and kinit as a client user.
   ASSERT_OK(kdc.CreateUserPrincipal("testuser"));
@@ -1120,22 +1130,28 @@ TEST_F(TestNegotiation, TestGSSAPIInvalidNegotiation) {
   CHECK_ERR(setenv("KRB5_KTNAME", kt_path.c_str(), 1 /*replace*/));
 
   RunNegotiationTest(
-      std::bind(RunGSSAPINegotiationServer, std::placeholders::_1,
-                [](const Status& s, ServerNegotiation& server) {
-                  CHECK(s.IsNotAuthorized());
+      [](unique_ptr<Socket> socket) {
+        RunGSSAPINegotiationServer(
+            std::move(socket),
+            [](const Status& s) {
+              CHECK(s.IsNotAuthorized());
 #ifndef KRB5_VERSION_LE_1_10
-                  ASSERT_STR_CONTAINS(s.ToString(),
-                                      "No key table entry found matching kudu/127.0.0.1");
+              ASSERT_STR_CONTAINS(s.ToString(),
+                                  "No key table entry found matching kudu/127.0.0.1");
 #endif
-                }),
-      std::bind(RunGSSAPINegotiationClient, std::placeholders::_1,
-                [](const Status& s, ClientNegotiation& client) {
-                  CHECK(s.IsNotAuthorized());
+            });
+      },
+      [](unique_ptr<Socket> socket) {
+        RunGSSAPINegotiationClient(
+            std::move(socket),
+            [](const Status& s) {
+              CHECK(s.IsNotAuthorized());
 #ifndef KRB5_VERSION_LE_1_10
-                  ASSERT_STR_CONTAINS(s.ToString(),
-                                      "No key table entry found matching kudu/127.0.0.1");
+              ASSERT_STR_CONTAINS(s.ToString(),
+                                  "No key table entry found matching kudu/127.0.0.1");
 #endif
-                }));
+            });
+      });
 }
 #endif
 
diff --git a/src/kudu/rpc/proxy.cc b/src/kudu/rpc/proxy.cc
index 85babdb..61eb997 100644
--- a/src/kudu/rpc/proxy.cc
+++ b/src/kudu/rpc/proxy.cc
@@ -17,12 +17,11 @@
 
 #include "kudu/rpc/proxy.h"
 
+#include <functional>
 #include <iostream>
 #include <memory>
 #include <utility>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
-#include <boost/ref.hpp>
 #include <glog/logging.h>
 
 #include "kudu/gutil/strings/substitute.h"
@@ -96,7 +95,7 @@ Status Proxy::SyncRequest(const string& method,
                           RpcController* controller) const {
   Notification note;
   AsyncRequest(method, req, DCHECK_NOTNULL(resp), controller,
-               boost::bind(&Notification::Notify, boost::ref(note)));
+               [&note]() { note.Notify(); });
   note.WaitForNotification();
   return controller->status();
 }
diff --git a/src/kudu/rpc/reactor-test.cc b/src/kudu/rpc/reactor-test.cc
index 2de5f58..0168f0e 100644
--- a/src/kudu/rpc/reactor-test.cc
+++ b/src/kudu/rpc/reactor-test.cc
@@ -15,10 +15,9 @@
 // specific language governing permissions and limitations
 // under the License.
 
+#include <functional>
 #include <memory>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
-#include <boost/function.hpp>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
 
@@ -57,10 +56,10 @@ class ReactorTest : public RpcTestBase {
     latch_.CountDown();
   }
 
-  void ScheduledTaskScheduleAgain(const Status& status) {
+  void ScheduledTaskScheduleAgain(const Status& /*status*/) {
+    auto* current = Thread::current_thread();
     messenger_->ScheduleOnReactor(
-        boost::bind(&ReactorTest::ScheduledTaskCheckThread, this, _1,
-                    Thread::current_thread()),
+        [=](const Status& s) { this->ScheduledTaskCheckThread(s, current); },
         MonoDelta::FromMilliseconds(0));
     latch_.CountDown();
   }
@@ -72,7 +71,7 @@ class ReactorTest : public RpcTestBase {
 
 TEST_F(ReactorTest, TestFunctionIsCalled) {
   messenger_->ScheduleOnReactor(
-      boost::bind(&ReactorTest::ScheduledTask, this, _1, Status::OK()),
+      [=](const Status& s) { this->ScheduledTask(s, Status::OK()); },
       MonoDelta::FromSeconds(0));
   latch_.Wait();
 }
@@ -80,7 +79,7 @@ TEST_F(ReactorTest, TestFunctionIsCalled) {
 TEST_F(ReactorTest, TestFunctionIsCalledAtTheRightTime) {
   MonoTime before = MonoTime::Now();
   messenger_->ScheduleOnReactor(
-      boost::bind(&ReactorTest::ScheduledTask, this, _1, Status::OK()),
+      [=](const Status& s) { this->ScheduledTask(s, Status::OK()); },
       MonoDelta::FromMilliseconds(100));
   latch_.Wait();
   MonoTime after = MonoTime::Now();
@@ -90,8 +89,7 @@ TEST_F(ReactorTest, TestFunctionIsCalledAtTheRightTime) {
 
 TEST_F(ReactorTest, TestFunctionIsCalledIfReactorShutdown) {
   messenger_->ScheduleOnReactor(
-      boost::bind(&ReactorTest::ScheduledTask, this, _1,
-                  Status::Aborted("doesn't matter")),
+      [=](const Status& s) { this->ScheduledTask(s, Status::Aborted("doesn't matter")); },
       MonoDelta::FromSeconds(60));
   messenger_->Shutdown();
   latch_.Wait();
@@ -102,7 +100,7 @@ TEST_F(ReactorTest, TestReschedulesOnSameReactorThread) {
   latch_.Reset(2);
 
   messenger_->ScheduleOnReactor(
-      boost::bind(&ReactorTest::ScheduledTaskScheduleAgain, this, _1),
+      [=](const Status& s) { this->ScheduledTaskScheduleAgain(s); },
       MonoDelta::FromSeconds(0));
   latch_.Wait();
   latch_.Wait();
diff --git a/src/kudu/rpc/reactor.cc b/src/kudu/rpc/reactor.cc
index dc87286..3b48653 100644
--- a/src/kudu/rpc/reactor.cc
+++ b/src/kudu/rpc/reactor.cc
@@ -28,9 +28,7 @@
 #include <string>
 #include <utility>
 
-#include <boost/bind.hpp>
 #include <boost/intrusive/list.hpp>
-#include <boost/ref.hpp>
 #include <ev++.h>
 #include <ev.h>
 #include <gflags/gflags.h>
@@ -62,10 +60,6 @@
 #include "kudu/util/threadpool.h"
 #include "kudu/util/trace.h"
 
-namespace boost {
-template <typename Signature> class function;
-}  // namespace boost
-
 // When compiling on Mac OS X, use 'kqueue' instead of the default, 'select', for the event loop.
 // Otherwise we run into problems because 'select' can't handle connections when more than 1024
 // file descriptors are open by the process.
@@ -154,7 +148,7 @@ void DoInitLibEv() {
 
 } // anonymous namespace
 
-ReactorThread::ReactorThread(Reactor *reactor, const MessengerBuilder& bld)
+ReactorThread::ReactorThread(Reactor* reactor, const MessengerBuilder& bld)
   : loop_(kDefaultLibEvFlags),
     cur_time_(MonoTime::Now()),
     last_unused_tcp_scan_(cur_time_),
@@ -432,7 +426,7 @@ void ReactorThread::TimerHandler(ev::timer& /*watcher*/, int revents) {
   ScanIdleConnections();
 }
 
-void ReactorThread::RegisterTimeout(ev::timer *watcher) {
+void ReactorThread::RegisterTimeout(ev::timer* watcher) {
   watcher->set(loop_);
 }
 
@@ -485,7 +479,7 @@ void ReactorThread::ScanIdleConnections() {
   VLOG_IF(1, shutdown > 0) << name() << ": shutdown " << shutdown << " TCP connections.";
 }
 
-const std::string& ReactorThread::name() const {
+const string& ReactorThread::name() const {
   return reactor_->name();
 }
 
@@ -493,7 +487,7 @@ MonoTime ReactorThread::cur_time() const {
   return cur_time_;
 }
 
-Reactor *ReactorThread::reactor() {
+Reactor* ReactorThread::reactor() {
   return reactor_;
 }
 
@@ -658,7 +652,7 @@ void ReactorThread::CompleteConnectionNegotiation(
   conn->EpollRegister(loop_);
 }
 
-Status ReactorThread::CreateClientSocket(Socket *sock) {
+Status ReactorThread::CreateClientSocket(Socket* sock) {
   Status ret = sock->Init(Socket::FLAG_NONBLOCKING);
   if (ret.ok()) {
     ret = sock->SetNoDelay(true);
@@ -669,7 +663,7 @@ Status ReactorThread::CreateClientSocket(Socket *sock) {
   return ret;
 }
 
-Status ReactorThread::StartConnect(Socket *sock, const Sockaddr& remote) {
+Status ReactorThread::StartConnect(Socket* sock, const Sockaddr& remote) {
   const Status ret = sock->Connect(remote);
   if (ret.ok()) {
     VLOG(3) << "StartConnect: connect finished immediately for " << remote.ToString();
@@ -687,7 +681,7 @@ Status ReactorThread::StartConnect(Socket *sock, const Sockaddr& remote) {
   return ret;
 }
 
-void ReactorThread::DestroyConnection(Connection *conn,
+void ReactorThread::DestroyConnection(Connection* conn,
                                       const Status& conn_status,
                                       unique_ptr<ErrorStatusPB> rpc_error) {
   DCHECK(IsCurrentThread());
@@ -718,7 +712,7 @@ void ReactorThread::DestroyConnection(Connection *conn,
   }
 }
 
-DelayedTask::DelayedTask(boost::function<void(const Status&)> func,
+DelayedTask::DelayedTask(std::function<void(const Status&)> func,
                          MonoDelta when)
     : func_(std::move(func)),
       when_(when),
@@ -799,7 +793,7 @@ Reactor::~Reactor() {
   Shutdown(Messenger::ShutdownMode::ASYNC);
 }
 
-const std::string& Reactor::name() const {
+const string& Reactor::name() const {
   return name_;
 }
 
@@ -811,7 +805,7 @@ bool Reactor::closing() const {
 // Task to call an arbitrary function within the reactor thread.
 class RunFunctionTask : public ReactorTask {
  public:
-  explicit RunFunctionTask(boost::function<Status()> f)
+  explicit RunFunctionTask(std::function<Status()> f)
       : function_(std::move(f)), latch_(1) {}
 
   void Run(ReactorThread* /*reactor*/) override {
@@ -831,25 +825,24 @@ class RunFunctionTask : public ReactorTask {
   }
 
  private:
-  boost::function<Status()> function_;
+  const std::function<Status()> function_;
   Status status_;
   CountDownLatch latch_;
 };
 
-Status Reactor::GetMetrics(ReactorMetrics *metrics) {
-  return RunOnReactorThread(boost::bind(&ReactorThread::GetMetrics, &thread_, metrics));
+Status Reactor::GetMetrics(ReactorMetrics* metrics) {
+  return RunOnReactorThread([&]() { return this->thread_.GetMetrics(metrics); });
 }
 
-Status Reactor::RunOnReactorThread(const boost::function<Status()>& f) {
-  RunFunctionTask task(f);
+Status Reactor::RunOnReactorThread(std::function<Status()> f) {
+  RunFunctionTask task(std::move(f));
   ScheduleReactorTask(&task);
   return task.Wait();
 }
 
 Status Reactor::DumpConnections(const DumpConnectionsRequestPB& req,
                                 DumpConnectionsResponsePB* resp) {
-  return RunOnReactorThread(boost::bind(&ReactorThread::DumpConnections,
-                                        &thread_, boost::ref(req), resp));
+  return RunOnReactorThread([&]() { return this->thread_.DumpConnections(req, resp); });
 }
 
 class RegisterConnectionTask : public ReactorTask {
@@ -871,10 +864,10 @@ class RegisterConnectionTask : public ReactorTask {
   }
 
  private:
-  scoped_refptr<Connection> conn_;
+  const scoped_refptr<Connection> conn_;
 };
 
-void Reactor::RegisterInboundSocket(Socket *socket, const Sockaddr& remote) {
+void Reactor::RegisterInboundSocket(Socket* socket, const Sockaddr& remote) {
   VLOG(3) << name_ << ": new inbound connection to " << remote.ToString();
   unique_ptr<Socket> new_socket(new Socket(socket->Release()));
   auto task = new RegisterConnectionTask(
@@ -902,17 +895,17 @@ class AssignOutboundCallTask : public ReactorTask {
   }
 
  private:
-  shared_ptr<OutboundCall> call_;
+  const shared_ptr<OutboundCall> call_;
 };
 
-void Reactor::QueueOutboundCall(const shared_ptr<OutboundCall>& call) {
+void Reactor::QueueOutboundCall(shared_ptr<OutboundCall> call) {
   DVLOG(3) << name_ << ": queueing outbound call "
            << call->ToString() << " to remote " << call->conn_id().remote().ToString();
   // Test cancellation when 'call_' is in 'READY' state.
   if (PREDICT_FALSE(call->ShouldInjectCancellation())) {
     QueueCancellation(call);
   }
-  ScheduleReactorTask(new AssignOutboundCallTask(call));
+  ScheduleReactorTask(new AssignOutboundCallTask(std::move(call)));
 }
 
 class CancellationTask : public ReactorTask {
@@ -930,14 +923,14 @@ class CancellationTask : public ReactorTask {
   }
 
  private:
-  shared_ptr<OutboundCall> call_;
+  const shared_ptr<OutboundCall> call_;
 };
 
-void Reactor::QueueCancellation(const shared_ptr<OutboundCall>& call) {
-  ScheduleReactorTask(new CancellationTask(call));
+void Reactor::QueueCancellation(shared_ptr<OutboundCall> call) {
+  ScheduleReactorTask(new CancellationTask(std::move(call)));
 }
 
-void Reactor::ScheduleReactorTask(ReactorTask *task) {
+void Reactor::ScheduleReactorTask(ReactorTask* task) {
   bool was_empty;
   {
     std::unique_lock<LockType> l(lock_);
@@ -955,7 +948,7 @@ void Reactor::ScheduleReactorTask(ReactorTask *task) {
   }
 }
 
-bool Reactor::DrainTaskQueue(boost::intrusive::list<ReactorTask> *tasks) { // NOLINT(*)
+bool Reactor::DrainTaskQueue(boost::intrusive::list<ReactorTask>* tasks) { // NOLINT(*)
   std::lock_guard<LockType> l(lock_);
   if (closing_) {
     return false;
diff --git a/src/kudu/rpc/reactor.h b/src/kudu/rpc/reactor.h
index 9d9f860..ae279fb 100644
--- a/src/kudu/rpc/reactor.h
+++ b/src/kudu/rpc/reactor.h
@@ -17,12 +17,12 @@
 #pragma once
 
 #include <cstdint>
+#include <functional>
 #include <list>
 #include <memory>
 #include <string>
 #include <unordered_map>
 
-#include <boost/function.hpp> // IWYU pragma: keep
 #include <boost/intrusive/list.hpp>
 #include <boost/intrusive/list_hook.hpp>
 #include <ev++.h>
@@ -40,10 +40,6 @@
 #include "kudu/util/status.h"
 #include "kudu/util/thread.h"
 
-namespace boost {
-template <typename Signature> class function;
-}  // namespace boost
-
 namespace kudu {
 
 class Sockaddr;
@@ -81,7 +77,7 @@ class ReactorTask : public boost::intrusive::list_base_hook<> {
   ReactorTask();
 
   // Run the task. 'reactor' is guaranteed to be the current thread.
-  virtual void Run(ReactorThread *reactor) = 0;
+  virtual void Run(ReactorThread* reactor) = 0;
 
   // Abort the task, in the case that the reactor shut down before the
   // task could be processed. This may or may not run on the reactor thread
@@ -89,7 +85,7 @@ class ReactorTask : public boost::intrusive::list_base_hook<> {
   //
   // The Reactor guarantees that the Reactor lock is free when this
   // method is called.
-  virtual void Abort(const Status &abort_status) {}
+  virtual void Abort(const Status& abort_status) {}
 
   virtual ~ReactorTask();
 
@@ -106,7 +102,7 @@ class ReactorTask : public boost::intrusive::list_base_hook<> {
 //    receives a Status as its first argument.
 class DelayedTask : public ReactorTask {
  public:
-  DelayedTask(boost::function<void(const Status &)> func, MonoDelta when);
+  DelayedTask(std::function<void(const Status&)> func, MonoDelta when);
 
   // Schedules the task for running later but doesn't actually run it yet.
   void Run(ReactorThread* thread) override;
@@ -119,7 +115,7 @@ class DelayedTask : public ReactorTask {
   void TimerHandler(ev::timer& watcher, int revents);
 
   // User function to invoke when timer fires or when task is aborted.
-  const boost::function<void(const Status&)> func_;
+  const std::function<void(const Status&)> func_;
 
   // Delay to apply to this task.
   const MonoDelta when_;
@@ -148,7 +144,7 @@ class ReactorThread {
                                   ConnectionIdHash, ConnectionIdEqual>
       conn_multimap_t;
 
-  ReactorThread(Reactor *reactor, const MessengerBuilder &bld);
+  ReactorThread(Reactor* reactor, const MessengerBuilder& bld);
 
   // This may be called from another thread.
   Status Init();
@@ -167,14 +163,14 @@ class ReactorThread {
   void WakeThread();
 
   // libev callback for handling async notifications in our epoll thread.
-  void AsyncHandler(ev::async &watcher, int revents);
+  void AsyncHandler(ev::async& watcher, int revents);
 
   // libev callback for handling timer events in our epoll thread.
-  void TimerHandler(ev::timer &watcher, int revents);
+  void TimerHandler(ev::timer& watcher, int revents);
 
   // Register an epoll timer watcher with our event loop.
   // Does not set a timeout or start it.
-  void RegisterTimeout(ev::timer *watcher);
+  void RegisterTimeout(ev::timer* watcher);
 
   // This may be called from another thread.
   const std::string &name() const;
@@ -245,7 +241,7 @@ class ReactorThread {
   // The connection is not explicitly deleted -- shared_ptr reference counting
   // may hold on to the object after this, but callers should assume that it
   // _may_ be deleted by this call.
-  void DestroyConnection(Connection *conn, const Status &conn_status,
+  void DestroyConnection(Connection* conn, const Status& conn_status,
                          std::unique_ptr<ErrorStatusPB> rpc_error = {});
 
   // Scan any open connections for idle ones that have been idle longer than
@@ -254,10 +250,10 @@ class ReactorThread {
   void ScanIdleConnections();
 
   // Create a new client socket (non-blocking, NODELAY)
-  static Status CreateClientSocket(Socket *sock);
+  static Status CreateClientSocket(Socket* sock);
 
   // Initiate a new connection on the given socket.
-  static Status StartConnect(Socket *sock, const Sockaddr &remote);
+  static Status StartConnect(Socket* sock, const Sockaddr& remote);
 
   // Assign a new outbound call to the appropriate connection object.
   // If this fails, the call is marked failed and completed.
@@ -268,7 +264,7 @@ class ReactorThread {
   // Also mark the call as slated for cancellation so the callback
   // may be invoked early if the RPC hasn't yet been sent or if it's
   // waiting for a response from the remote.
-  void CancelOutboundCall(const std::shared_ptr<OutboundCall> &call);
+  void CancelOutboundCall(const std::shared_ptr<OutboundCall>& call);
 
   // Register a new connection.
   void RegisterConnection(scoped_refptr<Connection> conn);
@@ -349,7 +345,7 @@ class Reactor {
  public:
   Reactor(std::shared_ptr<Messenger> messenger,
           int index,
-          const MessengerBuilder &bld);
+          const MessengerBuilder& bld);
   Status Init();
 
   // Shuts down the reactor and its corresponding thread, optionally waiting
@@ -358,10 +354,10 @@ class Reactor {
 
   ~Reactor();
 
-  const std::string &name() const;
+  const std::string& name() const;
 
   // Collect metrics about the reactor.
-  Status GetMetrics(ReactorMetrics *metrics);
+  Status GetMetrics(ReactorMetrics* metrics);
 
   // Add any connections on this reactor thread into the given status dump.
   Status DumpConnections(const DumpConnectionsRequestPB& req,
@@ -370,14 +366,14 @@ class Reactor {
   // Queue a new incoming connection. Takes ownership of the underlying fd from
   // 'socket', but not the Socket object itself.
   // If the reactor is already shut down, takes care of closing the socket.
-  void RegisterInboundSocket(Socket *socket, const Sockaddr &remote);
+  void RegisterInboundSocket(Socket* socket, const Sockaddr& remote);
 
   // Queue a new call to be sent. If the reactor is already shut down, marks
   // the call as failed.
-  void QueueOutboundCall(const std::shared_ptr<OutboundCall> &call);
+  void QueueOutboundCall(std::shared_ptr<OutboundCall> call);
 
   // Queue a new reactor task to cancel an outbound call.
-  void QueueCancellation(const std::shared_ptr<OutboundCall> &call);
+  void QueueCancellation(std::shared_ptr<OutboundCall> call);
 
   // Schedule the given task's Run() method to be called on the
   // reactor thread.
@@ -385,15 +381,15 @@ class Reactor {
   // called.
   // Does _not_ take ownership of 'task' -- the task should take care of
   // deleting itself after running if it is allocated on the heap.
-  void ScheduleReactorTask(ReactorTask *task);
+  void ScheduleReactorTask(ReactorTask* task);
 
-  Status RunOnReactorThread(const boost::function<Status()>& f);
+  Status RunOnReactorThread(std::function<Status()> f);
 
   // If the Reactor is closing, returns false.
   // Otherwise, drains the pending_tasks_ queue into the provided list.
-  bool DrainTaskQueue(boost::intrusive::list<ReactorTask> *tasks);
+  bool DrainTaskQueue(boost::intrusive::list<ReactorTask>* tasks);
 
-  Messenger *messenger() const {
+  Messenger* messenger() const {
     return messenger_.get();
   }
 
diff --git a/src/kudu/rpc/response_callback.h b/src/kudu/rpc/response_callback.h
index 5a70a79..98065b5 100644
--- a/src/kudu/rpc/response_callback.h
+++ b/src/kudu/rpc/response_callback.h
@@ -16,12 +16,12 @@
 // under the License.
 #pragma once
 
-#include <boost/function.hpp>
+#include <functional>
 
 namespace kudu {
 namespace rpc {
 
-typedef boost::function<void()> ResponseCallback;
+typedef std::function<void()> ResponseCallback;
 
 } // namespace rpc
 } // namespace kudu
diff --git a/src/kudu/rpc/retriable_rpc.h b/src/kudu/rpc/retriable_rpc.h
index aa0d207..3668b55 100644
--- a/src/kudu/rpc/retriable_rpc.h
+++ b/src/kudu/rpc/retriable_rpc.h
@@ -16,11 +16,10 @@
 // under the License.
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <string>
 
-#include <boost/bind.hpp>
-
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/gutil/strings/substitute.h"
 #include "kudu/rpc/messenger.h"
@@ -305,7 +304,7 @@ void RetriableRpc<Server, RequestPB, ResponsePB>::ReplicaFoundCb(const Status& s
 
   DCHECK_EQ(result.result, RetriableRpcStatus::OK);
   current_ = server;
-  Try(server, boost::bind(&RetriableRpc::SendRpcCb, this, Status::OK()));
+  Try(server, [this]() { this->SendRpcCb(Status::OK()); });
 }
 
 template <class Server, class RequestPB, class ResponsePB>
diff --git a/src/kudu/rpc/rpc-bench.cc b/src/kudu/rpc/rpc-bench.cc
index 782cb9c..dca665e 100644
--- a/src/kudu/rpc/rpc-bench.cc
+++ b/src/kudu/rpc/rpc-bench.cc
@@ -45,7 +45,6 @@
 #include "kudu/util/test_macros.h"
 #include "kudu/util/test_util.h"
 
-using std::bind;
 using std::shared_ptr;
 using std::string;
 using std::thread;
@@ -231,7 +230,7 @@ class ClientAsyncWorkload {
     proxy_->AddAsync(req_,
                      &resp_,
                      &controller_,
-                     bind(&ClientAsyncWorkload::CallOneRpc, this));
+                     [this]() { this->CallOneRpc(); });
   }
 
   void Start() {
diff --git a/src/kudu/rpc/rpc-test.cc b/src/kudu/rpc/rpc-test.cc
index 15ee5c4..67b51d8 100644
--- a/src/kudu/rpc/rpc-test.cc
+++ b/src/kudu/rpc/rpc-test.cc
@@ -29,8 +29,6 @@
 #include <unordered_map>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/ref.hpp>
 #include <gflags/gflags_declare.h>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
@@ -532,8 +530,7 @@ TEST_P(TestRpc, TestClientConnectionMetrics) {
     for (int i = 0; i < n_calls; i++) {
       controllers.emplace_back(new RpcController());
       p.AsyncRequest(GenericCalculatorService::kAddMethodName, add_req, &add_resp,
-                     controllers.back().get(), boost::bind(
-                         &CountDownLatch::CountDown, boost::ref(latch)));
+                     controllers.back().get(), [&latch]() { latch.CountDown(); });
     }
     auto cleanup = MakeScopedCleanup([&](){
       latch.Wait();
@@ -1074,7 +1071,7 @@ TEST_F(TestRpc, TestServerShutsDown) {
   for (int i = 0; i < n_calls; i++) {
     controllers.emplace_back(new RpcController());
     p.AsyncRequest(GenericCalculatorService::kAddMethodName, req, &resp, controllers.back().get(),
-                   boost::bind(&CountDownLatch::CountDown, boost::ref(latch)));
+                   [&latch]() { latch.CountDown(); });
   }
 
   // Accept the TCP connection.
@@ -1197,7 +1194,9 @@ TEST_P(TestRpc, TestRpcCallbackDestroysMessenger) {
   {
     Proxy p(client_messenger, bad_addr, "xxx-host", "xxx-service");
     p.AsyncRequest("my-fake-method", req, &resp, &controller,
-                   boost::bind(&DestroyMessengerCallback, &client_messenger, &latch));
+                   [&client_messenger, &latch]() {
+                     DestroyMessengerCallback(&client_messenger, &latch);
+                   });
   }
   latch.Wait();
 }
@@ -1419,9 +1418,10 @@ TEST_P(TestRpc, TestCancellationAsync) {
     req.set_sidecar_idx(idx);
 
     CountDownLatch latch(1);
+    auto* payload_raw = payload.get();
     p.AsyncRequest(GenericCalculatorService::kSleepWithSidecarMethodName,
                    req, &resp, &controller,
-                   boost::bind(SleepCallback, payload.get(), &latch));
+                   [payload_raw, &latch]() { SleepCallback(payload_raw, &latch); });
     // Sleep for a while before cancelling the RPC.
     if (i > 0) SleepFor(MonoDelta::FromMicroseconds(rand.Uniform64(i * 30)));
     controller.Cancel();
@@ -1459,7 +1459,7 @@ static void SendAndCancelRpcs(Proxy* p, const Slice& slice) {
     CountDownLatch latch(1);
     p->AsyncRequest(GenericCalculatorService::kPushStringsMethodName,
                     request, &resp, &controller,
-                    boost::bind(&CountDownLatch::CountDown, boost::ref(latch)));
+                    [&latch]() { latch.CountDown(); });
 
     if ((i++ % 8) != 0) {
       // Sleep for a while before cancelling the RPC.
diff --git a/src/kudu/rpc/rpc.cc b/src/kudu/rpc/rpc.cc
index 862e804..7701924 100644
--- a/src/kudu/rpc/rpc.cc
+++ b/src/kudu/rpc/rpc.cc
@@ -20,10 +20,9 @@
 #include <algorithm>
 #include <cmath>
 #include <cstdlib>
+#include <functional>
 #include <string>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
-#include <boost/function.hpp>
 #include <glog/logging.h>
 
 #include "kudu/gutil/strings/substitute.h"
@@ -51,7 +50,7 @@ void RpcRetrier::DelayedRetry(Rpc* rpc, const Status& why_status) {
   // RPC on our behalf.
   MonoDelta backoff = ComputeBackoff(attempt_num_++);
   messenger_->ScheduleOnReactor(
-      boost::bind(&RpcRetrier::DelayedRetryCb, this, rpc, _1), backoff);
+      [this, rpc](const Status& s) { this->DelayedRetryCb(rpc, s); }, backoff);
 }
 
 MonoDelta RpcRetrier::ComputeBackoff(int num_attempts) const {
diff --git a/src/kudu/rpc/rpc.h b/src/kudu/rpc/rpc.h
index 32f2112..747c5b5 100644
--- a/src/kudu/rpc/rpc.h
+++ b/src/kudu/rpc/rpc.h
@@ -16,6 +16,7 @@
 // under the License.
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <utility>
diff --git a/src/kudu/rpc/rpc_stub-test.cc b/src/kudu/rpc/rpc_stub-test.cc
index e199b23..59c7667 100644
--- a/src/kudu/rpc/rpc_stub-test.cc
+++ b/src/kudu/rpc/rpc_stub-test.cc
@@ -29,9 +29,6 @@
 #include <thread>
 #include <vector>
 
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-#include <boost/ref.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 #include <glog/stl_logging.h>
@@ -145,7 +142,7 @@ TEST_F(RpcStubTest, TestBigCallData) {
     controllers.emplace_back(new RpcController);
 
     p.EchoAsync(req, resps.back().get(), controllers.back().get(),
-                boost::bind(&CountDownLatch::CountDown, boost::ref(latch)));
+                [&latch]() { latch.CountDown(); });
   }
 
   latch.Wait();
@@ -318,7 +315,7 @@ TEST_F(RpcStubTest, TestCallWithMissingPBFieldClientSide) {
   // Request is missing the 'y' field.
   AddResponsePB resp;
   Atomic32 callback_count = 0;
-  p.AddAsync(req, &resp, &controller, boost::bind(&DoIncrement, &callback_count));
+  p.AddAsync(req, &resp, &controller, [&callback_count]() { DoIncrement(&callback_count); });
   while (NoBarrier_Load(&callback_count) == 0) {
     SleepFor(MonoDelta::FromMicroseconds(10));
   }
@@ -450,15 +447,17 @@ struct AsyncSleep {
 TEST_F(RpcStubTest, TestDontHandleTimedOutCalls) {
   CalculatorServiceProxy p(client_messenger_, server_addr_, server_addr_.host());
   vector<unique_ptr<AsyncSleep>> sleeps;
+  sleeps.reserve(n_worker_threads_);
 
   // Send enough sleep calls to occupy the worker threads.
   for (int i = 0; i < n_worker_threads_; i++) {
     unique_ptr<AsyncSleep> sleep(new AsyncSleep);
     sleep->rpc.set_timeout(MonoDelta::FromSeconds(1));
     sleep->req.set_sleep_micros(1000*1000); // 1sec
+    auto& l = sleep->latch;
     p.SleepAsync(sleep->req, &sleep->resp, &sleep->rpc,
-                 boost::bind(&CountDownLatch::CountDown, &sleep->latch));
-    sleeps.push_back(std::move(sleep));
+                 [&l]() { l.CountDown(); });
+    sleeps.emplace_back(std::move(sleep));
   }
 
   // We asynchronously sent the RPCs above, but the RPCs might still
@@ -586,8 +585,9 @@ TEST_F(RpcStubTest, TestDumpCallsInFlight) {
   CalculatorServiceProxy p(client_messenger_, server_addr_, server_addr_.host());
   AsyncSleep sleep;
   sleep.req.set_sleep_micros(100 * 1000); // 100ms
+  auto& l = sleep.latch;
   p.SleepAsync(sleep.req, &sleep.resp, &sleep.rpc,
-               boost::bind(&CountDownLatch::CountDown, &sleep.latch));
+               [&l]() { l.CountDown(); });
 
   // Check the running RPC status on the client messenger.
   DumpConnectionsRequestPB dump_req;
@@ -635,8 +635,9 @@ TEST_F(RpcStubTest, TestDumpSampledCalls) {
   sleeps[1].req.set_sleep_micros(1500 * 1000); // 1500ms
 
   for (auto& sleep : sleeps) {
+    auto& l = sleep.latch;
     p.SleepAsync(sleep.req, &sleep.resp, &sleep.rpc,
-                 boost::bind(&CountDownLatch::CountDown, &sleep.latch));
+                 [&l]() { l.CountDown(); });
   }
   for (auto& sleep : sleeps) {
     sleep.latch.Wait();
@@ -694,7 +695,7 @@ TEST_F(RpcStubTest, TestCallbackClearedAfterRunning) {
   req.set_y(20);
   AddResponsePB resp;
   p.AddAsync(req, &resp, &controller,
-             boost::bind(MyTestCallback, &latch, my_refptr));
+             [&latch, my_refptr]() { MyTestCallback(&latch, my_refptr); });
   latch.Wait();
 
   // The ref count should go back down to 1. However, we need to loop a little
diff --git a/src/kudu/security/init.cc b/src/kudu/security/init.cc
index 5431865..5a81a95 100644
--- a/src/kudu/security/init.cc
+++ b/src/kudu/security/init.cc
@@ -116,7 +116,7 @@ Status Krb5CallToStatus(krb5_context ctx, krb5_error_code code) {
 
   std::unique_ptr<const char, std::function<void(const char*)>> err_msg(
       krb5_get_error_message(ctx, code),
-      std::bind(krb5_free_error_message, ctx, std::placeholders::_1));
+      [ctx](const char* msg) { krb5_free_error_message(ctx, msg); });
   return Status::RuntimeError(err_msg.get());
 }
 #define KRB5_RETURN_NOT_OK_PREPEND(call, prepend) \
diff --git a/src/kudu/server/default_path_handlers.cc b/src/kudu/server/default_path_handlers.cc
index 712a5b4..694ec63 100644
--- a/src/kudu/server/default_path_handlers.cc
+++ b/src/kudu/server/default_path_handlers.cc
@@ -29,7 +29,6 @@
 #include <vector>
 
 #include <boost/algorithm/string/predicate.hpp>
-#include <boost/bind.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 
@@ -471,8 +470,10 @@ static void WriteMetricsAsJson(const MetricRegistry* const metrics,
 }
 
 void RegisterMetricsJsonHandler(Webserver* webserver, const MetricRegistry* const metrics) {
-  Webserver::PrerenderedPathHandlerCallback callback = boost::bind(WriteMetricsAsJson, metrics,
-                                                                   _1, _2);
+  auto callback = [metrics](const Webserver::WebRequest& req,
+                            Webserver::PrerenderedWebResponse* resp) {
+    WriteMetricsAsJson(metrics, req, resp);
+  };
   bool not_styled = false;
   bool not_on_nav_bar = false;
   bool is_on_nav_bar = true;
diff --git a/src/kudu/server/rpcz-path-handler.cc b/src/kudu/server/rpcz-path-handler.cc
index 1313aaf..528fa4e 100644
--- a/src/kudu/server/rpcz-path-handler.cc
+++ b/src/kudu/server/rpcz-path-handler.cc
@@ -17,13 +17,12 @@
 
 #include "kudu/server/rpcz-path-handler.h"
 
+#include <functional>
 #include <memory>
 #include <sstream>
 #include <string>
 #include <unordered_map>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
-
 #include "kudu/gutil/map-util.h"
 #include "kudu/gutil/strings/numbers.h"
 #include "kudu/rpc/messenger.h"
@@ -77,9 +76,12 @@ void RpczPathHandler(const shared_ptr<Messenger>& messenger,
 } // anonymous namespace
 
 void AddRpczPathHandlers(const shared_ptr<Messenger>& messenger, Webserver* webserver) {
-  webserver->RegisterPrerenderedPathHandler("/rpcz", "RPCs",
-                                            boost::bind(RpczPathHandler, messenger, _1, _2),
-                                            false, true);
+  webserver->RegisterPrerenderedPathHandler(
+      "/rpcz", "RPCs",
+      [messenger](const Webserver::WebRequest& req, Webserver::PrerenderedWebResponse* resp) {
+        RpczPathHandler(messenger, req, resp);
+      },
+      false, true);
 }
 
 } // namespace kudu
diff --git a/src/kudu/server/server_base.cc b/src/kudu/server/server_base.cc
index 604486a..610d51d 100644
--- a/src/kudu/server/server_base.cc
+++ b/src/kudu/server/server_base.cc
@@ -523,8 +523,9 @@ Status ServerBase::Init() {
   }
 
   RETURN_NOT_OK(builder.Build(&messenger_));
-  rpc_server_->set_too_busy_hook(std::bind(
-      &ServerBase::ServiceQueueOverflowed, this, std::placeholders::_1));
+  rpc_server_->set_too_busy_hook([this](rpc::ServicePool* pool) {
+    this->ServiceQueueOverflowed(pool);
+  });
 
   RETURN_NOT_OK(rpc_server_->Init(messenger_));
   RETURN_NOT_OK(rpc_server_->Bind());
diff --git a/src/kudu/server/tracing_path_handlers.cc b/src/kudu/server/tracing_path_handlers.cc
index a7eed20..842504b 100644
--- a/src/kudu/server/tracing_path_handlers.cc
+++ b/src/kudu/server/tracing_path_handlers.cc
@@ -16,14 +16,13 @@
 // under the License.
 #include "kudu/server/tracing_path_handlers.h"
 
+#include <functional>
 #include <map>
 #include <ostream>
 #include <string>
 #include <utility>
 #include <vector>
 
-
-#include <boost/bind.hpp> // IWYU pragma: keep
 #include <glog/logging.h>
 #include <rapidjson/document.h>
 
@@ -275,8 +274,10 @@ void TracingPathHandlers::RegisterHandlers(Webserver* server) {
   typedef pair<const string, Handler> HandlerPair;
   for (const HandlerPair& e : handlers) {
     server->RegisterPrerenderedPathHandler(
-      e.first, "",
-      boost::bind(&HandleRequest, e.second, _1, _2),
+        e.first, "", [e](const Webserver::WebRequest& req,
+                         Webserver::PrerenderedWebResponse* resp) {
+          HandleRequest(e.second, req, resp);
+        },
       false /* styled */, false /* is_on_nav_bar */);
   }
 }
diff --git a/src/kudu/server/webserver.cc b/src/kudu/server/webserver.cc
index fbd13b9..0f75752 100644
--- a/src/kudu/server/webserver.cc
+++ b/src/kudu/server/webserver.cc
@@ -36,7 +36,6 @@
 #include <vector>
 
 #include <boost/algorithm/string/case_conv.hpp>
-#include <boost/function.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 #include <mustache.h>
@@ -185,8 +184,8 @@ Webserver::~Webserver() {
   STLDeleteValues(&path_handlers_);
 }
 
-void Webserver::RootHandler(const Webserver::WebRequest& /* args */,
-                            Webserver::WebResponse* resp) {
+void Webserver::RootHandler(const WebRequest& /* args */,
+                            WebResponse* resp) {
   EasyJson path_handlers = resp->output.Set("path_handlers", EasyJson::kArray);
   for (const PathHandlerMap::value_type& handler : path_handlers_) {
     if (handler.second->is_on_nav_bar()) {
@@ -361,11 +360,10 @@ Status Webserver::Start() {
     return Status::RuntimeError(err_msg);
   }
 
-  PathHandlerCallback default_callback =
-      std::bind<void>(std::mem_fn(&Webserver::RootHandler),
-                      this, std::placeholders::_1, std::placeholders::_2);
-
-  RegisterPathHandler("/", "Home", default_callback,
+  RegisterPathHandler("/", "Home",
+                      [this](const WebRequest& req, WebResponse* resp) {
+                        this->RootHandler(req, resp);
+                      },
                       /*is_styled=*/true, /*is_on_nav_bar=*/true);
 
   vector<Sockaddr> addrs;
diff --git a/src/kudu/tablet/tablet_replica.cc b/src/kudu/tablet/tablet_replica.cc
index 28599d9..1903d49 100644
--- a/src/kudu/tablet/tablet_replica.cc
+++ b/src/kudu/tablet/tablet_replica.cc
@@ -654,10 +654,9 @@ Status TabletReplica::StartFollowerTransaction(const scoped_refptr<ConsensusRoun
   RETURN_NOT_OK(NewReplicaTransactionDriver(std::move(transaction), &driver));
 
   // A raw pointer is required to avoid a refcount cycle.
+  auto* driver_raw = driver.get();
   state->consensus_round()->SetConsensusReplicatedCallback(
-      std::bind(&TransactionDriver::ReplicationFinished,
-                driver.get(),
-                std::placeholders::_1));
+      [driver_raw](const Status& s) { driver_raw->ReplicationFinished(s); });
 
   RETURN_NOT_OK(driver->ExecuteAsync());
   return Status::OK();
diff --git a/src/kudu/tablet/transactions/transaction_driver.cc b/src/kudu/tablet/transactions/transaction_driver.cc
index 0391936..45b9730 100644
--- a/src/kudu/tablet/transactions/transaction_driver.cc
+++ b/src/kudu/tablet/transactions/transaction_driver.cc
@@ -168,9 +168,7 @@ Status TransactionDriver::Init(unique_ptr<Transaction> transaction,
       // A raw pointer is required to avoid a refcount cycle.
       mutable_state()->set_consensus_round(
         consensus_->NewRound(std::move(replicate_msg),
-                             std::bind(&TransactionDriver::ReplicationFinished,
-                                       this,
-                                       std::placeholders::_1)));
+                             [this](const Status& s) { this->ReplicationFinished(s); }));
     }
   }
 
diff --git a/src/kudu/tools/ksck_remote-test.cc b/src/kudu/tools/ksck_remote-test.cc
index a4dfe73..63f81e9 100644
--- a/src/kudu/tools/ksck_remote-test.cc
+++ b/src/kudu/tools/ksck_remote-test.cc
@@ -157,8 +157,6 @@ class RemoteKsckTest : public KuduTest {
   }
 
   // Writes rows to the table until the continue_writing flag is set to false.
-  //
-  // Public for use with boost::bind.
   void GenerateRowWritesLoop(CountDownLatch* started_writing,
                              const AtomicBool& continue_writing,
                              Promise<Status>* promise) {
diff --git a/src/kudu/tools/ksck_remote.cc b/src/kudu/tools/ksck_remote.cc
index 69e24b3..6414e84 100644
--- a/src/kudu/tools/ksck_remote.cc
+++ b/src/kudu/tools/ksck_remote.cc
@@ -24,7 +24,6 @@
 #include <mutex>
 #include <ostream>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
 #include <boost/optional/optional.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
@@ -277,7 +276,7 @@ void RemoteKsckTabletServer::FetchCurrentTimestampAsync() {
   generic_proxy_->ServerClockAsync(cb->req,
                                    &cb->resp,
                                    &cb->rpc,
-                                   boost::bind(&ServerClockResponseCallback::Run, cb));
+                                   [cb]() { cb->Run(); });
 }
 
 Status RemoteKsckTabletServer::FetchCurrentTimestamp() {
@@ -469,10 +468,11 @@ class ChecksumStepper {
         LOG(FATAL) << "Unknown type";
         break;
     }
-    unique_ptr<ChecksumCallbackHandler> handler(new ChecksumCallbackHandler(this));
-    rpc::ResponseCallback cb = boost::bind(&ChecksumCallbackHandler::Run, handler.get());
+
+    // 'handler' deletes itself when complete.
+    auto* handler = new ChecksumCallbackHandler(this);
+    rpc::ResponseCallback cb = [handler]() { handler->Run(); };
     proxy_->ChecksumAsync(req_, &resp_, &rpc_, cb);
-    ignore_result(handler.release());
   }
 
   const Schema schema_;
diff --git a/src/kudu/tools/tool_action_common.cc b/src/kudu/tools/tool_action_common.cc
index df71ca0..19feffe 100644
--- a/src/kudu/tools/tool_action_common.cc
+++ b/src/kudu/tools/tool_action_common.cc
@@ -180,11 +180,6 @@ using std::vector;
 using strings::Split;
 using strings::Substitute;
 
-namespace boost {
-template <typename Signature>
-class function;
-} // namespace boost
-
 namespace kudu {
 namespace tools {
 
@@ -802,10 +797,10 @@ template<typename Req, typename Resp>
 Status LeaderMasterProxy::SyncRpc(const Req& req,
                                   Resp* resp,
                                   string func_name,
-                                  const boost::function<void(master::MasterServiceProxy*,
-                                                             const Req&, Resp*,
-                                                             rpc::RpcController*,
-                                                             const ResponseCallback&)>& func) {
+                                  const std::function<void(master::MasterServiceProxy*,
+                                                           const Req&, Resp*,
+                                                           rpc::RpcController*,
+                                                           const ResponseCallback&)>& func) {
   MonoTime deadline = MonoTime::Now() + MonoDelta::FromMilliseconds(FLAGS_timeout_ms);
   Synchronizer sync;
   AsyncLeaderMasterRpc<Req, Resp> rpc(deadline, client_.get(), BackoffType::EXPONENTIAL,
@@ -820,41 +815,41 @@ Status LeaderMasterProxy::SyncRpc(
     const master::ChangeTServerStateRequestPB& req,
     master::ChangeTServerStateResponsePB* resp,
     string func_name,
-    const boost::function<void(MasterServiceProxy*,
-                               const master::ChangeTServerStateRequestPB&,
-                               master::ChangeTServerStateResponsePB*,
-                               RpcController*,
-                               const ResponseCallback&)>& func);
+    const std::function<void(MasterServiceProxy*,
+                             const master::ChangeTServerStateRequestPB&,
+                             master::ChangeTServerStateResponsePB*,
+                             RpcController*,
+                             const ResponseCallback&)>& func);
 template
 Status LeaderMasterProxy::SyncRpc(
     const master::ListTabletServersRequestPB& req,
     master::ListTabletServersResponsePB* resp,
     string func_name,
-    const boost::function<void(MasterServiceProxy*,
-                               const master::ListTabletServersRequestPB&,
-                               master::ListTabletServersResponsePB*,
-                               RpcController*,
-                               const ResponseCallback&)>& func);
+    const std::function<void(MasterServiceProxy*,
+                             const master::ListTabletServersRequestPB&,
+                             master::ListTabletServersResponsePB*,
+                             RpcController*,
+                             const ResponseCallback&)>& func);
 template
 Status LeaderMasterProxy::SyncRpc(
     const master::ListMastersRequestPB& req,
     master::ListMastersResponsePB* resp,
     string func_name,
-    const boost::function<void(MasterServiceProxy*,
-                               const master::ListMastersRequestPB&,
-                               master::ListMastersResponsePB*,
-                               RpcController*,
-                               const ResponseCallback&)>& func);
+    const std::function<void(MasterServiceProxy*,
+                             const master::ListMastersRequestPB&,
+                             master::ListMastersResponsePB*,
+                             RpcController*,
+                             const ResponseCallback&)>& func);
 template
 Status LeaderMasterProxy::SyncRpc(
     const master::ReplaceTabletRequestPB& req,
     master::ReplaceTabletResponsePB* resp,
     string func_name,
-    const boost::function<void(MasterServiceProxy*,
-                               const master::ReplaceTabletRequestPB&,
-                               master::ReplaceTabletResponsePB*,
-                               RpcController*,
-                               const ResponseCallback&)>& func);
+    const std::function<void(MasterServiceProxy*,
+                             const master::ReplaceTabletRequestPB&,
+                             master::ReplaceTabletResponsePB*,
+                             RpcController*,
+                             const ResponseCallback&)>& func);
 
 } // namespace tools
 } // namespace kudu
diff --git a/src/kudu/tools/tool_action_common.h b/src/kudu/tools/tool_action_common.h
index dfd2b99..5631212 100644
--- a/src/kudu/tools/tool_action_common.h
+++ b/src/kudu/tools/tool_action_common.h
@@ -14,10 +14,10 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-
 #pragma once
 
 #include <cstdint>
+#include <functional>
 #include <memory>
 #include <ostream>
 #include <string>
@@ -29,11 +29,6 @@
 #include "kudu/rpc/response_callback.h"
 #include "kudu/util/status.h"
 
-namespace boost {
-template <typename Signature>
-class function;
-} // namespace boost
-
 namespace kudu {
 
 class MonoDelta;
@@ -247,10 +242,10 @@ class LeaderMasterProxy {
   Status SyncRpc(const Req& req,
                  Resp* resp,
                  std::string func_name,
-                 const boost::function<void(master::MasterServiceProxy*,
-                                            const Req&, Resp*,
-                                            rpc::RpcController*,
-                                            const rpc::ResponseCallback&)>& func)
+                 const std::function<void(master::MasterServiceProxy*,
+                                          const Req&, Resp*,
+                                          rpc::RpcController*,
+                                          const rpc::ResponseCallback&)>& func)
       WARN_UNUSED_RESULT;
 
  private:
diff --git a/src/kudu/tserver/tablet_server-test.cc b/src/kudu/tserver/tablet_server-test.cc
index c336af8..c47a5e9 100644
--- a/src/kudu/tserver/tablet_server-test.cc
+++ b/src/kudu/tserver/tablet_server-test.cc
@@ -36,7 +36,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp>
 #include <boost/optional/optional.hpp>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
@@ -3649,7 +3648,7 @@ TEST_F(TabletServerTest, TestConcurrentDeleteTablet) {
   for (int i = 0; i < kNumDeletes; i++) {
     SCOPED_TRACE(SecureDebugString(req));
     admin_proxy_->DeleteTabletAsync(req, &responses[i], &rpcs[i],
-                                    boost::bind(&CountDownLatch::CountDown, &latch));
+                                    [&]() { latch.CountDown(); });
   }
   latch.Wait();
 
diff --git a/src/kudu/tserver/tablet_service.cc b/src/kudu/tserver/tablet_service.cc
index 6f0b6eb..c72d223 100644
--- a/src/kudu/tserver/tablet_service.cc
+++ b/src/kudu/tserver/tablet_service.cc
@@ -105,7 +105,6 @@
 #include "kudu/util/process_memory.h"
 #include "kudu/util/slice.h"
 #include "kudu/util/status.h"
-#include "kudu/util/status_callback.h"
 #include "kudu/util/stopwatch.h"
 #include "kudu/util/trace.h"
 #include "kudu/util/trace_metrics.h"
@@ -399,18 +398,6 @@ void HandleResponse(const ReqType* req, RespType* resp,
   context->RespondSuccess();
 }
 
-template <class ReqType, class RespType>
-static StatusCallback BindHandleResponse(
-    const ReqType* req,
-    RespType* resp,
-    RpcContext* context) {
-  return std::bind(&HandleResponse<ReqType, RespType>,
-                   req,
-                   resp,
-                   context,
-                   std::placeholders::_1);
-}
-
 } // namespace
 
 typedef ListTabletsResponsePB::StatusAndSchemaPB StatusAndSchemaPB;
@@ -1540,7 +1527,11 @@ void ConsensusServiceImpl::ChangeConfig(const ChangeConfigRequestPB* req,
   shared_ptr<RaftConsensus> consensus;
   if (!GetConsensusOrRespond(replica, resp, context, &consensus)) return;
   boost::optional<TabletServerErrorPB::Code> error_code;
-  Status s = consensus->ChangeConfig(*req, BindHandleResponse(req, resp, context), &error_code);
+  Status s = consensus->ChangeConfig(
+      *req, [req, resp, context](const Status& s) {
+        HandleResponse(req, resp, context, s);
+      },
+      &error_code);
   if (PREDICT_FALSE(!s.ok())) {
     HandleErrorResponse(req, resp, context, error_code, s);
     return;
@@ -1564,7 +1555,11 @@ void ConsensusServiceImpl::BulkChangeConfig(const BulkChangeConfigRequestPB* req
   shared_ptr<RaftConsensus> consensus;
   if (!GetConsensusOrRespond(replica, resp, context, &consensus)) return;
   boost::optional<TabletServerErrorPB::Code> error_code;
-  Status s = consensus->BulkChangeConfig(*req, BindHandleResponse(req, resp, context), &error_code);
+  Status s = consensus->BulkChangeConfig(
+      *req, [req, resp, context](const Status& s) {
+        HandleResponse(req, resp, context, s);
+      },
+      &error_code);
   if (PREDICT_FALSE(!s.ok())) {
     HandleErrorResponse(req, resp, context, error_code, s);
     return;
diff --git a/src/kudu/tserver/tserver_path_handlers.cc b/src/kudu/tserver/tserver_path_handlers.cc
index c93cbba..ffc0c1b 100644
--- a/src/kudu/tserver/tserver_path_handlers.cc
+++ b/src/kudu/tserver/tserver_path_handlers.cc
@@ -17,9 +17,9 @@
 
 #include "kudu/tserver/tserver_path_handlers.h"
 
-#include <stdint.h>
-
 #include <algorithm>
+#include <cstdint>
+#include <functional>
 #include <iosfwd>
 #include <map>
 #include <memory>
@@ -29,7 +29,6 @@
 #include <utility>
 #include <vector>
 
-#include <boost/bind.hpp> // IWYU pragma: keep
 #include <glog/logging.h>
 
 #include "kudu/common/common.pb.h"
@@ -192,39 +191,57 @@ TabletServerPathHandlers::~TabletServerPathHandlers() {
 Status TabletServerPathHandlers::Register(Webserver* server) {
   server->RegisterPathHandler(
     "/scans", "Scans",
-    boost::bind(&TabletServerPathHandlers::HandleScansPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleScansPage(req, resp);
+    },
     true /* styled */, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablets", "Tablets",
-    boost::bind(&TabletServerPathHandlers::HandleTabletsPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleTabletsPage(req, resp);
+    },
     true /* styled */, true /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablet", "",
-    boost::bind(&TabletServerPathHandlers::HandleTabletPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleTabletPage(req, resp);
+    },
     true /* styled */, false /* is_on_nav_bar */);
   server->RegisterPrerenderedPathHandler(
     "/transactions", "",
-    boost::bind(&TabletServerPathHandlers::HandleTransactionsPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::PrerenderedWebResponse* resp) {
+      this->HandleTransactionsPage(req, resp);
+    },
     true /* styled */, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablet-rowsetlayout-svg", "",
-    boost::bind(&TabletServerPathHandlers::HandleTabletSVGPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleTabletSVGPage(req, resp);
+    },
     true /* styled */, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablet-consensus-status", "",
-    boost::bind(&TabletServerPathHandlers::HandleConsensusStatusPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleConsensusStatusPage(req, resp);
+    },
     true /* styled */, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/log-anchors", "",
-    boost::bind(&TabletServerPathHandlers::HandleLogAnchorsPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleLogAnchorsPage(req, resp);
+    },
     true /* styled */, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/dashboards", "Dashboards",
-    boost::bind(&TabletServerPathHandlers::HandleDashboardsPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleDashboardsPage(req, resp);
+    },
     true /* styled */, true /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/maintenance-manager", "",
-    boost::bind(&TabletServerPathHandlers::HandleMaintenanceManagerPage, this, _1, _2),
+    [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
+      this->HandleMaintenanceManagerPage(req, resp);
+    },
     true /* styled */, false /* is_on_nav_bar */);
 
   return Status::OK();
diff --git a/src/kudu/util/debug/trace_event_impl.cc b/src/kudu/util/debug/trace_event_impl.cc
index a03808c..b091caf 100644
--- a/src/kudu/util/debug/trace_event_impl.cc
+++ b/src/kudu/util/debug/trace_event_impl.cc
@@ -1363,7 +1363,7 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter,
 
       Status s = Thread::CreateWithFlags(
           "tracing", "sampler",
-          std::bind(&TraceSamplingThread::ThreadMain,sampling_thread_.get()),
+          [this]() { this->sampling_thread_->ThreadMain(); },
           Thread::NO_STACK_WATCHDOG, &sampling_thread_handle_);
       if (!s.ok()) {
         LOG(DFATAL) << "failed to create trace sampling thread: " << s.ToString();
diff --git a/src/kudu/util/url-coding.cc b/src/kudu/util/url-coding.cc
index 96a6a0f..a80720d 100644
--- a/src/kudu/util/url-coding.cc
+++ b/src/kudu/util/url-coding.cc
@@ -21,6 +21,7 @@
 #include <cctype>
 #include <cstddef>
 #include <exception>
+#include <functional>
 #include <iterator>
 #include <sstream>
 
@@ -29,7 +30,6 @@
 #include <boost/archive/iterators/binary_from_base64.hpp>
 #include <boost/archive/iterators/transform_width.hpp>
 #include <boost/iterator/iterator_facade.hpp>
-#include <boost/function.hpp>
 #include <glog/logging.h>
 
 using boost::archive::iterators::base64_from_binary;
@@ -44,11 +44,11 @@ namespace kudu {
 // characters it will encode.
 // See common/src/java/org/apache/hadoop/hive/common/FileUtils.java
 // in the Hive source code for the source of this list.
-static boost::function<bool (char)> HiveShouldEscape = boost::is_any_of("\"#%\\*/:=?\u00FF"); // NOLINT(*)
+static std::function<bool (char)> HiveShouldEscape = boost::is_any_of("\"#%\\*/:=?\u00FF"); // NOLINT(*)
 
 // It is more convenient to maintain the complement of the set of
 // characters to escape when not in Hive-compat mode.
-static boost::function<bool (char)> ShouldNotEscape = boost::is_any_of("-_.~"); // NOLINT(*)
+static std::function<bool (char)> ShouldNotEscape = boost::is_any_of("-_.~"); // NOLINT(*)
 
 static inline void UrlEncode(const char* in, int in_len, string* out, bool hive_compat) {
   (*out).reserve(in_len);
diff --git a/src/kudu/util/web_callback_registry.h b/src/kudu/util/web_callback_registry.h
index 3b7ff13..ee72e4d 100644
--- a/src/kudu/util/web_callback_registry.h
+++ b/src/kudu/util/web_callback_registry.h
@@ -14,15 +14,13 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-
 #pragma once
 
+#include <functional>
 #include <sstream>
 #include <string>
 #include <unordered_map>
 
-#include <boost/function.hpp>
-
 #include "kudu/util/easy_json.h"
 
 namespace kudu {
@@ -93,12 +91,12 @@ class WebCallbackRegistry {
 
   // A function that handles an HTTP request where the response body will be rendered
   // with a mustache template from the JSON object held by 'resp'.
-  typedef boost::function<void (const WebRequest& args, WebResponse* resp)>
+  typedef std::function<void (const WebRequest& args, WebResponse* resp)>
       PathHandlerCallback;
 
   // A function that handles an HTTP request, where the response body is the contents
   // of the 'output' member of 'resp'.
-  typedef boost::function<void (const WebRequest& args, PrerenderedWebResponse* resp)>
+  typedef std::function<void (const WebRequest& args, PrerenderedWebResponse* resp)>
       PrerenderedPathHandlerCallback;
 
   virtual ~WebCallbackRegistry() {}