diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc
index d8522df9340539eb2230992622951fa31b0706ae..a725236e26c9e6aba2b5177a2f183fc8fe6cde33 100644
--- a/src/box/lua/tuple.cc
+++ b/src/box/lua/tuple.cc
@@ -111,14 +111,6 @@ lbox_tuple_gc(struct lua_State *L)
 	return 0;
 }
 
-static int
-lbox_tuple_len(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	lua_pushnumber(L, tuple_arity(tuple));
-	return 1;
-}
-
 static int
 lbox_tuple_slice(struct lua_State *L)
 {
@@ -425,37 +417,6 @@ lbox_tuple_totable(struct lua_State *L)
 	return 1;
 }
 
-/**
- * Implementation of tuple __index metamethod.
- *
- * Provides operator [] access to individual fields for integer
- * indexes, as well as searches and invokes metatable methods
- * for strings.
- */
-static int
-lbox_tuple_index(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	/* For integer indexes, implement [] operator */
-	if (lua_isnumber(L, 2)) {
-		int i = luaL_checkint(L, 2);
-		const char *field = tuple_field(tuple, i);
-		if (field == NULL) {
-			const char *data = tuple->data;
-			luaL_error(L, "%s: index %d is out of bounds (0..%d)",
-				   tuplelib_name, i, mp_decode_array(&data));
-		}
-		luamp_decode(L, &field);
-		return 1;
-	}
-
-	/* If we got a string, try to find a method for it. */
-	const char *sz = luaL_checkstring(L, 2);
-	lua_getmetatable(L, 1);
-	lua_getfield(L, -1, sz);
-	return 1;
-}
-
 void
 lbox_pushtuple(struct lua_State *L, struct tuple *tuple)
 {
@@ -524,8 +485,6 @@ lbox_tuple_pairs(struct lua_State *L)
 
 static const struct luaL_reg lbox_tuple_meta[] = {
 	{"__gc", lbox_tuple_gc},
-	{"__len", lbox_tuple_len},
-	{"__index", lbox_tuple_index},
 	{"next", lbox_tuple_next},
 	{"pairs", lbox_tuple_pairs},
 	{"slice", lbox_tuple_slice},
diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua
index c2989d2897f30f87076755eb5865330f158b7ae1..f53f17a21eef84672c36a06662ef37be7cf9ef03 100644
--- a/src/box/lua/tuple.lua
+++ b/src/box/lua/tuple.lua
@@ -2,6 +2,7 @@
 
 local ffi = require('ffi')
 local yaml = require('yaml')
+local msgpackffi = require('msgpackffi')
 
 ffi.cdef([[
 struct tuple
@@ -12,6 +13,11 @@ struct tuple
     uint32_t _bsize;
     char data[0];
 } __attribute__((packed));
+
+uint32_t
+tuple_arity(const struct tuple *tuple);
+const char *
+tuple_field(const struct tuple *tuple, uint32_t i);
 ]])
 
 local builtin = ffi.C
@@ -33,10 +39,19 @@ local methods = {
 
 local tuple_gc = cfuncs.__gc;
 
-local tuple_field = cfuncs.__index
+local tuple_field = function(tuple, field_n)
+    local field = builtin.tuple_field(tuple, field_n)
+    if field == nil then
+        return nil
+    end
+    return msgpackffi.decode_unchecked(field)
+end
+
 ffi.metatype('struct tuple', {
     __gc = tuple_gc;
-    __len = cfuncs.__len;
+    __len = function(tuple)
+        return builtin.tuple_arity(tuple)
+    end;
     __tostring = function(tuple)
         -- Unpack tuple, call yaml.encode, remove yaml header and footer
         -- 5 = '---\n\n' (header), -6 = '\n...\n' (footer)
diff --git a/src/box/tuple.h b/src/box/tuple.h
index 5f1f62e01e8d70eaf34c16d3b753b7dc65133a9f..58a0a649311fdf7a516e8231ba3cb999b7a49385 100644
--- a/src/box/tuple.h
+++ b/src/box/tuple.h
@@ -206,7 +206,7 @@ tuple_format(const struct tuple *tuple)
  * @param tuple
  * @return the number of fields in tuple
  */
-static inline uint32_t
+extern "C" inline uint32_t
 tuple_arity(const struct tuple *tuple)
 {
 	const char *data = tuple->data;
@@ -220,7 +220,7 @@ tuple_arity(const struct tuple *tuple)
  * @pre field < tuple->field_count.
  * @returns field data if field exists or NULL
  */
-static inline const char *
+inline const char *
 tuple_field_old(const struct tuple_format *format,
 		const struct tuple *tuple, uint32_t i)
 {
@@ -261,7 +261,7 @@ tuple_field_old(const struct tuple_format *format,
  *        or NULL if field is out of range
  * @param len pointer where the len of the field will be stored
  */
-inline const char *
+extern "C" inline const char *
 tuple_field(const struct tuple *tuple, uint32_t i)
 {
 	return tuple_field_old(tuple_format(tuple), tuple, i);
diff --git a/src/ffisyms.cc b/src/ffisyms.cc
index f7346dce0eb49c67ea1a03b7c053625ad93e81a8..523b9a9284732573033ba81a1cfadc0498700712 100644
--- a/src/ffisyms.cc
+++ b/src/ffisyms.cc
@@ -1,5 +1,6 @@
 #include <bit/bit.h>
 #include <lib/msgpuck/msgpuck.h>
+#include <box/tuple.h>
 
 /*
  * A special hack to cc/ld to keep symbols in an optimized binary.
@@ -9,5 +10,7 @@ void *ffi_symbols[] = {
 	(void *) bswap_u32,
 	(void *) bswap_u64,
 	(void *) mp_bswap_float,
-	(void *) mp_bswap_double
+	(void *) mp_bswap_double,
+	(void *) tuple_arity,
+	(void *) tuple_field
 };