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

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 9515e8b36 refactor(bindings/C): Implement error with error message 
(#3250)
9515e8b36 is described below

commit 9515e8b3648a2634f59feb1b0c1cdda6c9de1676
Author: Xinyou Ji <[email protected]>
AuthorDate: Fri Oct 13 08:14:51 2023 -0400

    refactor(bindings/C): Implement error with error message (#3250)
    
    * Implement opendal_error
    
    * docs
    
    * tests
    
    * error msg example
    
    * free
    
    * separate the valgrind test w/ original test
    
    * docs
    
    * add opendal_operator_new result
    
    * refactor the test to use new operator init logic
    
    * refactor the examples to use the new operator_ptr init logic
    
    * add .gitignore
    
    * add error_msg tests to valgrind
    
    * rename basicrw.c to basic.c
    
    * resolve comments
    
    * fix fmt
    
    * Fix zig
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix go
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix swift
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Format code
    
    Signed-off-by: Xuanwo <[email protected]>
    
    ---------
    
    Signed-off-by: Xuanwo <[email protected]>
    Co-authored-by: Xuanwo <[email protected]>
---
 .github/workflows/bindings_c.yml                   |   6 +-
 bindings/c/.gitignore                              |   7 +
 bindings/c/Makefile                                |   8 +
 bindings/c/README.md                               |   6 +-
 bindings/c/examples/.gitignore                     |   1 -
 bindings/c/examples/{basicrw.c => basic.c}         |  13 +-
 bindings/c/examples/{basicrw.c => error_handle.c}  |  35 ++-
 bindings/c/include/opendal.h                       | 240 ++++++++++++---------
 bindings/c/src/error.rs                            |  83 +++++--
 bindings/c/src/lib.rs                              | 202 ++++++++++-------
 bindings/c/src/result.rs                           |  50 +++--
 bindings/c/src/types.rs                            |  10 +-
 bindings/c/tests/bdd.cpp                           |  27 +--
 bindings/c/tests/error_msg.cpp                     |  72 +++++++
 bindings/c/tests/list.cpp                          |  15 +-
 bindings/go/opendal.go                             |  37 +++-
 bindings/go/opendal_test.go                        |   4 +-
 bindings/swift/.swift-format                       |  11 +
 bindings/swift/OpenDAL/Package.swift               |  11 +-
 .../OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift     |  20 +-
 .../swift/OpenDAL/Sources/OpenDAL/Operator.swift   |  95 ++++----
 .../OpenDAL/Tests/OpenDALTests/OpenDALTests.swift  |  23 +-
 bindings/zig/README.md                             |   2 +-
 bindings/zig/src/opendal.zig                       |   3 -
 bindings/zig/test/bdd.zig                          |  14 +-
 25 files changed, 638 insertions(+), 357 deletions(-)

diff --git a/.github/workflows/bindings_c.yml b/.github/workflows/bindings_c.yml
index 7bcac37cc..dccfcda03 100644
--- a/.github/workflows/bindings_c.yml
+++ b/.github/workflows/bindings_c.yml
@@ -64,6 +64,10 @@ jobs:
       - name: Check diff
         run: git diff --exit-code
 
-      - name: Build and Run BDD tests
+      - name: Build and Run tests
         working-directory: "bindings/c"
         run: make test
+
+      - name: Build and Run memory-leak tests
+        working-directory: "bindings/c"
+        run: make memory_leak
diff --git a/bindings/c/.gitignore b/bindings/c/.gitignore
index 8f5beed0e..355533f65 100644
--- a/bindings/c/.gitignore
+++ b/bindings/c/.gitignore
@@ -2,3 +2,10 @@ build/
 docs/
 
 *.o
+
+examples/*
+!examples/
+!examples/*.h
+!examples/*.c
+!examples/*.cc
+!examples/*.cpp
\ No newline at end of file
diff --git a/bindings/c/Makefile b/bindings/c/Makefile
index 0337f4d90..e10f47bdf 100644
--- a/bindings/c/Makefile
+++ b/bindings/c/Makefile
@@ -44,8 +44,16 @@ build:
 test:
        $(CXX) tests/bdd.cpp -o $(OBJ_DIR)/bdd $(CXXFLAGS) $(LDFLAGS) $(LIBS)
        $(CXX) tests/list.cpp -o $(OBJ_DIR)/list $(CXXFLAGS) $(LDFLAGS) $(LIBS)
+       $(CXX) tests/error_msg.cpp -o $(OBJ_DIR)/error_msg $(CXXFLAGS) 
$(LDFLAGS) $(LIBS)
+       $(OBJ_DIR)/bdd
+       $(OBJ_DIR)/list
+       $(OBJ_DIR)/error_msg
+
+.PHONY: test_memory_leak
+memory_leak:
        $(VALGRIND) $(OBJ_DIR)/bdd
        $(VALGRIND) $(OBJ_DIR)/list
+       $(VALGRIND) $(OBJ_DIR)/error_msg
 
 .PHONY: doc
 doc:
diff --git a/bindings/c/README.md b/bindings/c/README.md
index 7d57c27fc..f89cead26 100644
--- a/bindings/c/README.md
+++ b/bindings/c/README.md
@@ -24,13 +24,13 @@ int main()
     };
 
     /* Write this into path "/testpath" */
-    opendal_code code = opendal_operator_blocking_write(op, "/testpath", data);
-    assert(code == OPENDAL_OK);
+    opendal_error *error = opendal_operator_blocking_write(op, "/testpath", 
data);
+    assert(error == NULL);
 
     /* We can read it out, make sure the data is the same */
     opendal_result_read r = opendal_operator_blocking_read(op, "/testpath");
     opendal_bytes* read_bytes = r.data;
-    assert(r.code == OPENDAL_OK);
+    assert(r.error == NULL);
     assert(read_bytes->len == 24);
 
     /* Lets print it out */
diff --git a/bindings/c/examples/.gitignore b/bindings/c/examples/.gitignore
deleted file mode 100644
index 887b0251a..000000000
--- a/bindings/c/examples/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-basicrw
diff --git a/bindings/c/examples/basicrw.c b/bindings/c/examples/basic.c
similarity index 83%
copy from bindings/c/examples/basicrw.c
copy to bindings/c/examples/basic.c
index 3c3cd75cd..88071847c 100644
--- a/bindings/c/examples/basicrw.c
+++ b/bindings/c/examples/basic.c
@@ -24,8 +24,11 @@
 int main()
 {
     /* Initialize a operator for "memory" backend, with no options */
-    const opendal_operator_ptr *op = opendal_operator_new("memory", 0);
-    assert(op->ptr != NULL);
+    opendal_result_operator_new result = opendal_operator_new("memory", 0);
+    assert(result.operator_ptr != NULL);
+    assert(result.error == NULL);
+
+    opendal_operator_ptr* op = result.operator_ptr;
 
     /* Prepare some data to be written */
     opendal_bytes data = {
@@ -34,13 +37,13 @@ int main()
     };
 
     /* Write this into path "/testpath" */
-    opendal_code code = opendal_operator_blocking_write(op, "/testpath", data);
-    assert(code == OPENDAL_OK);
+    opendal_error* error = opendal_operator_blocking_write(op, "/testpath", 
data);
+    assert(error == NULL);
 
     /* We can read it out, make sure the data is the same */
     opendal_result_read r = opendal_operator_blocking_read(op, "/testpath");
     opendal_bytes* read_bytes = r.data;
-    assert(r.code == OPENDAL_OK);
+    assert(r.error == NULL);
     assert(read_bytes->len == 24);
 
     /* Lets print it out */
diff --git a/bindings/c/examples/basicrw.c b/bindings/c/examples/error_handle.c
similarity index 58%
rename from bindings/c/examples/basicrw.c
rename to bindings/c/examples/error_handle.c
index 3c3cd75cd..3bb3308e7 100644
--- a/bindings/c/examples/basicrw.c
+++ b/bindings/c/examples/error_handle.c
@@ -21,36 +21,29 @@
 #include "opendal.h"
 #include "stdio.h"
 
+// this example shows how to get error message from opendal_error
 int main()
 {
     /* Initialize a operator for "memory" backend, with no options */
-    const opendal_operator_ptr *op = opendal_operator_new("memory", 0);
-    assert(op->ptr != NULL);
+    opendal_result_operator_new result = opendal_operator_new("memory", 0);
+    assert(result.operator_ptr != NULL);
+    assert(result.error == NULL);
 
-    /* Prepare some data to be written */
-    opendal_bytes data = {
-        .data = (uint8_t*)"this_string_length_is_24",
-        .len = 24,
-    };
+    opendal_operator_ptr* op = result.operator_ptr;
 
-    /* Write this into path "/testpath" */
-    opendal_code code = opendal_operator_blocking_write(op, "/testpath", data);
-    assert(code == OPENDAL_OK);
-
-    /* We can read it out, make sure the data is the same */
+    /* The read is supposed to fail */
     opendal_result_read r = opendal_operator_blocking_read(op, "/testpath");
-    opendal_bytes* read_bytes = r.data;
-    assert(r.code == OPENDAL_OK);
-    assert(read_bytes->len == 24);
+    assert(r.error != NULL);
+    assert(r.error->code == OPENDAL_NOT_FOUND);
 
-    /* Lets print it out */
-    for (int i = 0; i < 24; ++i) {
-        printf("%c", read_bytes->data[i]);
+    /* Lets print the error message out */
+    struct opendal_bytes* error_msg = &r.error->message;
+    for (int i = 0; i < error_msg->len; ++i) {
+        printf("%c", error_msg->data[i]);
     }
-    printf("\n");
 
-    /* the opendal_bytes read is heap allocated, please free it */
-    opendal_bytes_free(read_bytes);
+    /* free the error since the error is not NULL */
+    opendal_error_free(r.error);
 
     /* the operator_ptr is also heap allocated */
     opendal_operator_free(op);
diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index 95c0be52d..51d66aadb 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -31,14 +31,6 @@
  * added in the future.
  */
 typedef enum opendal_code {
-  /**
-   * All is well
-   */
-  OPENDAL_OK,
-  /**
-   * General error
-   */
-  OPENDAL_ERROR,
   /**
    * returning it back. For example, s3 returns an internal service error.
    */
@@ -230,24 +222,6 @@ typedef struct opendal_operator_ptr {
   const struct BlockingOperator *ptr;
 } opendal_operator_ptr;
 
-/**
- * \brief The configuration for the initialization of opendal_operator_ptr.
- *
- * \note This is also a heap-allocated struct, please free it after you use it
- *
- * @see opendal_operator_new has an example of using opendal_operator_options
- * @see opendal_operator_options_new This function construct the operator
- * @see opendal_operator_options_free This function frees the heap memory of 
the operator
- * @see opendal_operator_options_set This function allow you to set the options
- */
-typedef struct opendal_operator_options {
-  /**
-   * The pointer to the Rust HashMap<String, String>
-   * Only touch this on judging whether it is NULL.
-   */
-  struct HashMap_String__String *inner;
-} opendal_operator_options;
-
 /**
  * \brief opendal_bytes carries raw-bytes with its length
  *
@@ -268,13 +242,72 @@ typedef struct opendal_bytes {
   uintptr_t len;
 } opendal_bytes;
 
+/**
+ * \brief The opendal error type for C binding, containing an error code and 
corresponding error
+ * message.
+ *
+ * The normal operations returns a pointer to the opendal_error, and the 
**nullptr normally
+ * represents no error has taken placed**. If any error has taken place, the 
caller should check
+ * the error code and print the error message.
+ *
+ * The error code is represented in opendal_code, which is a enum on different 
type of errors.
+ * The error messages is represented in opendal_bytes, which is a non-null 
terminated byte array.
+ *
+ * \note 1. The error message is on heap, so the error needs to be freed by 
the caller, by calling
+ *       opendal_error_free. 2. The error message is not null terminated, so 
the caller should
+ *       never use "%s" to print the error message.
+ *
+ * @see opendal_code
+ * @see opendal_bytes
+ * @see opendal_error_free
+ */
+typedef struct opendal_error {
+  enum opendal_code code;
+  struct opendal_bytes message;
+} opendal_error;
+
+/**
+ * \brief The result type returned by opendal_operator_new() operation.
+ *
+ * If the init logic is successful, the `operator_ptr` field will be set to a 
valid
+ * pointer, and the `error` field will be set to null. If the init logic 
fails, the
+ * `operator_ptr` field will be set to null, and the `error` field will be set 
to a
+ * valid pointer with error code and error message.
+ *
+ * @see opendal_operator_new()
+ * @see opendal_operator_ptr
+ * @see opendal_error
+ */
+typedef struct opendal_result_operator_new {
+  struct opendal_operator_ptr *operator_ptr;
+  struct opendal_error *error;
+} opendal_result_operator_new;
+
+/**
+ * \brief The configuration for the initialization of opendal_operator_ptr.
+ *
+ * \note This is also a heap-allocated struct, please free it after you use it
+ *
+ * @see opendal_operator_new has an example of using opendal_operator_options
+ * @see opendal_operator_options_new This function construct the operator
+ * @see opendal_operator_options_free This function frees the heap memory of 
the operator
+ * @see opendal_operator_options_set This function allow you to set the options
+ */
+typedef struct opendal_operator_options {
+  /**
+   * The pointer to the Rust HashMap<String, String>
+   * Only touch this on judging whether it is NULL.
+   */
+  struct HashMap_String__String *inner;
+} opendal_operator_options;
+
 /**
  * \brief The result type returned by opendal's read operation.
  *
  * The result type of read operation in opendal C binding, it contains
- * the data that the read operation returns and a error code.
+ * the data that the read operation returns and an NULL error.
  * If the read operation failed, the `data` fields should be a nullptr
- * and the error code is **NOT** OPENDAL_OK.
+ * and the error is not NULL.
  */
 typedef struct opendal_result_read {
   /**
@@ -282,17 +315,17 @@ typedef struct opendal_result_read {
    */
   struct opendal_bytes *data;
   /**
-   * The error code, should be OPENDAL_OK if succeeds
+   * The error, if ok, it is null
    */
-  enum opendal_code code;
+  struct opendal_error *error;
 } opendal_result_read;
 
 /**
  * \brief The result type returned by opendal_operator_is_exist().
  *
  * The result type for opendal_operator_is_exist(), the field `is_exist`
- * contains whether the path exists, and the field `code` contains the
- * corresponding error code.
+ * contains whether the path exists, and the field `error` contains the
+ * corresponding error. If successful, the `error` field is null.
  *
  * \note If the opendal_operator_is_exist() fails, the `is_exist` field
  * will be set to false.
@@ -303,9 +336,9 @@ typedef struct opendal_result_is_exist {
    */
   bool is_exist;
   /**
-   * The error code, should be OPENDAL_OK if succeeds
+   * The error, if ok, it is null
    */
-  enum opendal_code code;
+  struct opendal_error *error;
 } opendal_result_is_exist;
 
 /**
@@ -331,7 +364,8 @@ typedef struct opendal_metadata {
  * \brief The result type returned by opendal_operator_stat().
  *
  * The result type for opendal_operator_stat(), the field `meta` contains the 
metadata
- * of the path, the field `code` represents whether the stat operation is 
successful.
+ * of the path, the field `error` represents whether the stat operation is 
successful.
+ * If successful, the `error` field is null.
  */
 typedef struct opendal_result_stat {
   /**
@@ -339,9 +373,9 @@ typedef struct opendal_result_stat {
    */
   struct opendal_metadata *meta;
   /**
-   * The error code, should be OPENDAL_OK if succeeds
+   * The error, if ok, it is null
    */
-  enum opendal_code code;
+  struct opendal_error *error;
 } opendal_result_stat;
 
 /**
@@ -361,12 +395,18 @@ typedef struct opendal_blocking_lister {
  * \brief The result type returned by opendal_operator_blocking_list().
  *
  * The result type for opendal_operator_blocking_list(), the field `lister` 
contains the lister
- * of the path, which is an iterator of the objects under the path. the field 
`code` represents
- * whether the stat operation is successful.
+ * of the path, which is an iterator of the objects under the path. the field 
`error` represents
+ * whether the stat operation is successful. If successful, the `error` field 
is null.
  */
 typedef struct opendal_result_list {
+  /**
+   * The lister, used for further listing operations
+   */
   struct opendal_blocking_lister *lister;
-  enum opendal_code code;
+  /**
+   * The error, if ok, it is null
+   */
+  struct opendal_error *error;
 } opendal_result_list;
 
 /**
@@ -397,10 +437,9 @@ extern "C" {
  * @param options the pointer to the options for this operators, it could be 
NULL, which means no
  * option is set
  * @see opendal_operator_options
- * @return A valid opendal_operator_ptr setup with the `scheme` and `options` 
is the construction
- * succeeds. A null opendal_operator_ptr if any error happens.
- *
- * \remark You may use the `ptr` field of opendal_operator_ptr to check if it 
is NULL.
+ * @return A valid opendal_result_operator_new setup with the `scheme` and 
`options` is the construction
+ * succeeds. On success the operator_ptr field is a valid pointer to a newly 
allocated opendal_operator_ptr,
+ * and the error field is NULL. Otherwise, the operator_ptr field is a NULL 
pointer and the error field.
  *
  * # Example
  *
@@ -412,7 +451,8 @@ extern "C" {
  * opendal_operator_options_set(options, "root", "/myroot");
  *
  * // Construct the operator based on the options and scheme
- * const opendal_operator_ptr *ptr = opendal_operator_new("memory", options);
+ * opendal_result_operator_new result = opendal_operator_new("memory", 
options);
+ * opendal_operator_ptr* op = result.operator_ptr;
  *
  * // you could free the options right away since the options is not used 
afterwards
  * opendal_operator_options_free(options);
@@ -422,30 +462,27 @@ extern "C" {
  *
  * # Safety
  *
- * It is **safe** under two cases below
- * * The memory pointed to by `scheme` contain a valid nul terminator at the 
end of
- *   the string.
- * * The `scheme` points to NULL, this function simply returns you a null 
opendal_operator_ptr.
+ * The only unsafe case is passing a invalid c string pointer to the `scheme` 
argument.
  */
-const struct opendal_operator_ptr *opendal_operator_new(const char *scheme,
+struct opendal_result_operator_new opendal_operator_new(const char *scheme,
                                                         const struct 
opendal_operator_options *options);
 
 /**
  * \brief Blockingly write raw bytes to `path`.
  *
- * Write the `bytes` into the `path` blockingly by `op_ptr`, returns the 
opendal_code OPENDAL_OK
- * if succeeds, others otherwise.
+ * Write the `bytes` into the `path` blockingly by `op_ptr`.
+ * Error is NULL if successful, otherwise it contains the error code and error 
message.
  *
- * @NOTE It is important to notice that the `bytes` that is passes in will be 
consumed by this
- *       function.
+ * \note It is important to notice that the `bytes` that is passes in will be 
consumed by this
+ *       function. Therefore, you should not use the `bytes` after this 
function returns.
  *
  * @param ptr The opendal_operator_ptr created previously
  * @param path The designated path you want to write your bytes in
  * @param bytes The opendal_byte typed bytes to be written
  * @see opendal_operator_ptr
  * @see opendal_bytes
- * @see opendal_code
- * @return OPENDAL_OK if succeeds others otherwise
+ * @see opendal_error
+ * @return NULL if succeeds, otherwise it contains the error code and error 
message.
  *
  * # Example
  *
@@ -458,10 +495,10 @@ const struct opendal_operator_ptr 
*opendal_operator_new(const char *scheme,
  * opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
  *
  * // now you can write!
- * opendal_code code = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
+ * opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
  *
  * // Assert that this succeeds
- * assert(code == OPENDAL_OK)
+ * assert(err == NULL);
  * ```
  *
  * # Safety
@@ -476,24 +513,23 @@ const struct opendal_operator_ptr 
*opendal_operator_new(const char *scheme,
  *
  * * If the `path` points to NULL, this function panics, i.e. exits with 
information
  */
-enum opendal_code opendal_operator_blocking_write(const struct 
opendal_operator_ptr *ptr,
-                                                  const char *path,
-                                                  struct opendal_bytes bytes);
+struct opendal_error *opendal_operator_blocking_write(const struct 
opendal_operator_ptr *ptr,
+                                                      const char *path,
+                                                      struct opendal_bytes 
bytes);
 
 /**
  * \brief Blockingly read the data from `path`.
  *
- * Read the data out from `path` blockingly by operator, returns
- * an opendal_result_read with error code.
+ * Read the data out from `path` blockingly by operator.
  *
  * @param ptr The opendal_operator_ptr created previously
  * @param path The path you want to read the data out
  * @see opendal_operator_ptr
  * @see opendal_result_read
- * @see opendal_code
+ * @see opendal_error
  * @return Returns opendal_result_read, the `data` field is a pointer to a 
newly allocated
- * opendal_bytes, the `code` field contains the error code. If the `code` is 
not OPENDAL_OK,
- * the `data` field points to NULL.
+ * opendal_bytes, the `error` field contains the error. If the `error` is not 
NULL, then
+ * the operation failed and the `data` field is a nullptr.
  *
  * \note If the read operation succeeds, the returned opendal_bytes is newly 
allocated on heap.
  * After your usage of that, please call opendal_bytes_free() to free the 
space.
@@ -505,7 +541,7 @@ enum opendal_code opendal_operator_blocking_write(const 
struct opendal_operator_
  * // ... you have write "Hello, World!" to path "/testpath"
  *
  * opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath");
- * assert(r.code == OPENDAL_OK);
+ * assert(r.error == NULL);
  *
  * opendal_bytes *bytes = r.data;
  * assert(bytes->len == 13);
@@ -527,14 +563,14 @@ struct opendal_result_read 
opendal_operator_blocking_read(const struct opendal_o
 /**
  * \brief Blockingly delete the object in `path`.
  *
- * Delete the object in `path` blockingly by `op_ptr`, returns the 
opendal_code OPENDAL_OK
- * if succeeds, others otherwise
+ * Delete the object in `path` blockingly by `op_ptr`.
+ * Error is NULL if successful, otherwise it contains the error code and error 
message.
  *
  * @param ptr The opendal_operator_ptr created previously
  * @param path The designated path you want to delete
  * @see opendal_operator_ptr
- * @see opendal_code
- * @return OPENDAL_OK if succeeds others otherwise
+ * @see opendal_error
+ * @return NULL if succeeds, otherwise it contains the error code and error 
message.
  *
  * # Example
  *
@@ -545,13 +581,15 @@ struct opendal_result_read 
opendal_operator_blocking_read(const struct opendal_o
  * // prepare your data
  * char* data = "Hello, World!";
  * opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
- * opendal_code code = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
+ * opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
+ *
+ * assert(error == NULL);
  *
  * // now you can delete!
- * opendal_code code = opendal_operator_blocking_delete(ptr, "/testpath");
+ * opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath");
  *
  * // Assert that this succeeds
- * assert(code == OPENDAL_OK)
+ * assert(error == NULL);
  * ```
  *
  * # Safety
@@ -564,37 +602,35 @@ struct opendal_result_read 
opendal_operator_blocking_read(const struct opendal_o
  *
  * * If the `path` points to NULL, this function panics, i.e. exits with 
information
  */
-enum opendal_code opendal_operator_blocking_delete(const struct 
opendal_operator_ptr *ptr,
-                                                   const char *path);
+struct opendal_error *opendal_operator_blocking_delete(const struct 
opendal_operator_ptr *ptr,
+                                                       const char *path);
 
 /**
  * \brief Check whether the path exists.
  *
  * If the operation succeeds, no matter the path exists or not,
- * the error code should be opendal_code::OPENDAL_OK. Otherwise,
- * the field `is_exist` is filled with false, and the error code
- * is set correspondingly.
+ * the error should be a nullptr. Otherwise, the field `is_exist`
+ * is filled with false, and the error is set
  *
  * @param ptr The opendal_operator_ptr created previously
  * @param path The path you want to check existence
  * @see opendal_operator_ptr
  * @see opendal_result_is_exist
- * @see opendal_code
+ * @see opendal_error
  * @return Returns opendal_result_is_exist, the `is_exist` field contains 
whether the path exists.
- * However, it the operation fails, the `is_exist` will contains false and the 
error code will be
- * stored in the `code` field.
+ * However, it the operation fails, the `is_exist` will contains false and the 
error will be set.
  *
  * # Example
  *
  * ```C
  * // .. you previously wrote some data to path "/mytest/obj"
  * opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/mytest/obj");
- * assert(e.code == OPENDAL_OK);
+ * assert(e.error == NULL);
  * assert(e.is_exist);
  *
  * // but you previously did **not** write any data to path "/yourtest/obj"
- * opendal_result_is_exist e = opendal_operator_is_exist(ptr, "yourtest/obj");
- * assert(e.code == OPENDAL_OK);
+ * opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/yourtest/obj");
+ * assert(e.error == NULL);
  * assert(!e.is_exist);
  * ```
  *
@@ -614,26 +650,24 @@ struct opendal_result_is_exist 
opendal_operator_is_exist(const struct opendal_op
 /**
  * \brief Stat the path, return its metadata.
  *
- * If the operation succeeds, the error code should be
- * OPENDAL_OK. Otherwise, the field `meta` is filled with
- * a NULL pointer, and the error code is set correspondingly.
+ * Error is NULL if successful, otherwise it contains the error code and error 
message.
  *
  * @param ptr The opendal_operator_ptr created previously
  * @param path The path you want to stat
  * @see opendal_operator_ptr
  * @see opendal_result_stat
  * @see opendal_metadata
- * @return Returns opendal_result_stat, containing a metadata and a 
opendal_code.
+ * @return Returns opendal_result_stat, containing a metadata and an 
opendal_error.
  * If the operation succeeds, the `meta` field would holds a valid metadata and
- * the `code` field should hold OPENDAL_OK. Otherwise the metadata will 
contain a
- * NULL pointer, i.e. invalid, and the `code` will be set correspondingly.
+ * the `error` field should hold nullptr. Otherwise the metadata will contain a
+ * NULL pointer, i.e. invalid, and the `error` will be set correspondingly.
  *
  * # Example
  *
  * ```C
  * // ... previously you wrote "Hello, World!" to path "/testpath"
  * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- * assert(s.code == OPENDAL_OK);
+ * assert(s.error == NULL);
  *
  * const opendal_metadata *meta = s.meta;
  *
@@ -664,7 +698,10 @@ struct opendal_result_stat opendal_operator_stat(const 
struct opendal_operator_p
  * @param ptr The opendal_operator_ptr created previously
  * @param path The designated path you want to delete
  * @see opendal_blocking_lister
- * @return
+ * @return Returns opendal_result_list, containing a lister and an 
opendal_error.
+ * If the operation succeeds, the `lister` field would holds a valid lister and
+ * the `error` field should hold nullptr. Otherwise the `lister`` will contain 
a
+ * NULL pointer, i.e. invalid, and the `error` will be set correspondingly.
  *
  * # Example
  *
@@ -673,7 +710,7 @@ struct opendal_result_stat opendal_operator_stat(const 
struct opendal_operator_p
  * // You have written some data into some files path "root/dir1"
  * // Your opendal_operator_ptr was called ptr
  * opendal_result_list l = opendal_operator_blocking_list(ptr, "root/dir1");
- * assert(l.code == OPENDAL_OK);
+ * assert(l.error == ERROR);
  *
  * opendal_blocking_lister *lister = l.lister;
  * opendal_list_entry *entry;
@@ -704,6 +741,11 @@ struct opendal_result_stat opendal_operator_stat(const 
struct opendal_operator_p
 struct opendal_result_list opendal_operator_blocking_list(const struct 
opendal_operator_ptr *ptr,
                                                           const char *path);
 
+/**
+ * \brief Frees the opendal_error, ok to call on NULL
+ */
+void opendal_error_free(struct opendal_error *ptr);
+
 /**
  * \brief Free the heap-allocated operator pointed by opendal_operator_ptr.
  *
@@ -740,7 +782,7 @@ void opendal_metadata_free(struct opendal_metadata *ptr);
  * ```C
  * // ... previously you wrote "Hello, World!" to path "/testpath"
  * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- * assert(s.code == OPENDAL_OK);
+ * assert(s.error == NULL);
  *
  * opendal_metadata *meta = s.meta;
  * assert(opendal_metadata_content_length(meta) == 13);
@@ -755,7 +797,7 @@ uint64_t opendal_metadata_content_length(const struct 
opendal_metadata *self);
  * ```C
  * // ... previously you wrote "Hello, World!" to path "/testpath"
  * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- * assert(s.code == OPENDAL_OK);
+ * assert(s.error == NULL);
  *
  * opendal_metadata *meta = s.meta;
  * assert(opendal_metadata_is_file(meta));
@@ -770,7 +812,7 @@ bool opendal_metadata_is_file(const struct opendal_metadata 
*self);
  * ```C
  * // ... previously you wrote "Hello, World!" to path "/testpath"
  * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- * assert(s.code == OPENDAL_OK);
+ * assert(s.error == NULL);
  *
  * opendal_metadata *meta = s.meta;
  *
@@ -842,7 +884,7 @@ void opendal_lister_free(const struct 
opendal_blocking_lister *p);
  *
  * Path is relative to operator's root. Only valid in current operator.
  *
- * @NOTE To free the string, you can directly call free()
+ * \note To free the string, you can directly call free()
  */
 char *opendal_list_entry_path(const struct opendal_list_entry *self);
 
@@ -853,7 +895,7 @@ char *opendal_list_entry_path(const struct 
opendal_list_entry *self);
  * If this entry is a dir, `Name` MUST endswith `/`
  * Otherwise, `Name` MUST NOT endswith `/`.
  *
- * @NOTE To free the string, you can directly call free()
+ * \note To free the string, you can directly call free()
  */
 char *opendal_list_entry_name(const struct opendal_list_entry *self);
 
diff --git a/bindings/c/src/error.rs b/bindings/c/src/error.rs
index 8aa8c87ca..366d646ae 100644
--- a/bindings/c/src/error.rs
+++ b/bindings/c/src/error.rs
@@ -17,20 +17,19 @@
 
 use ::opendal as od;
 
-/// The wrapper type for opendal's error, wrapped because of the
-/// orphan rule
-struct opendal_error(od::Error);
+use crate::types::opendal_bytes;
+
+/// \brief The wrapper type for opendal's Rust core error, wrapped because of 
the
+/// orphan rule.
+///
+/// \note User should never use this type directly, use [`opendal_error`] 
instead.
+struct raw_error(od::Error);
 
 /// \brief The error code for all opendal APIs in C binding.
 /// \todo The error handling is not complete, the error with error message 
will be
 /// added in the future.
 #[repr(C)]
-pub enum opendal_code {
-    /// All is well
-    OPENDAL_OK,
-    /// General error
-    // \todo: make details in the `opendal_error *`
-    OPENDAL_ERROR,
+pub(crate) enum opendal_code {
     /// returning it back. For example, s3 returns an internal service error.
     OPENDAL_UNEXPECTED,
     /// Underlying service doesn't support this operation.
@@ -53,14 +52,7 @@ pub enum opendal_code {
     OPENDAL_IS_SAME_FILE,
 }
 
-impl opendal_code {
-    pub(crate) fn from_opendal_error(e: od::Error) -> Self {
-        let error = opendal_error(e);
-        error.error_code()
-    }
-}
-
-impl opendal_error {
+impl raw_error {
     /// Convert the [`od::ErrorKind`] of [`od::Error`] to [`opendal_code`]
     pub(crate) fn error_code(&self) -> opendal_code {
         let e = &self.0;
@@ -81,3 +73,60 @@ impl opendal_error {
         }
     }
 }
+
+/// \brief The opendal error type for C binding, containing an error code and 
corresponding error
+/// message.
+///
+/// The normal operations returns a pointer to the opendal_error, and the 
**nullptr normally
+/// represents no error has taken placed**. If any error has taken place, the 
caller should check
+/// the error code and print the error message.
+///
+/// The error code is represented in opendal_code, which is a enum on 
different type of errors.
+/// The error messages is represented in opendal_bytes, which is a non-null 
terminated byte array.
+///
+/// \note 1. The error message is on heap, so the error needs to be freed by 
the caller, by calling
+///       opendal_error_free. 2. The error message is not null terminated, so 
the caller should
+///       never use "%s" to print the error message.
+///
+/// @see opendal_code
+/// @see opendal_bytes
+/// @see opendal_error_free
+#[repr(C)]
+pub struct opendal_error {
+    code: opendal_code,
+    message: opendal_bytes,
+}
+
+impl opendal_error {
+    // The caller should sink the error to heap memory and return the pointer
+    // that will not be freed by rustc
+    pub(crate) fn from_opendal_error(error: od::Error) -> Self {
+        let error = raw_error(error);
+        let code = error.error_code();
+        let c_str = format!("{}", error.0);
+        let message = opendal_bytes::new(c_str.into_bytes());
+        opendal_error { code, message }
+    }
+
+    pub(crate) fn manual_error(code: opendal_code, message: String) -> Self {
+        let message = opendal_bytes::new(message.into_bytes());
+        opendal_error { code, message }
+    }
+
+    /// \brief Frees the opendal_error, ok to call on NULL
+    #[no_mangle]
+    pub unsafe extern "C" fn opendal_error_free(ptr: *mut opendal_error) {
+        if !ptr.is_null() {
+            let message_ptr = &(*ptr).message as *const opendal_bytes as *mut 
opendal_bytes;
+            if !message_ptr.is_null() {
+                let data_mut = unsafe { (*message_ptr).data as *mut u8 };
+                let _ = unsafe {
+                    Vec::from_raw_parts(data_mut, (*message_ptr).len, 
(*message_ptr).len)
+                };
+            }
+
+            // free the pointer
+            let _ = unsafe { Box::from_raw(ptr) };
+        }
+    }
+}
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs
index 6535b7965..013e2a307 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/lib.rs
@@ -34,10 +34,11 @@ use std::os::raw::c_char;
 use std::str::FromStr;
 
 use ::opendal as od;
+use error::opendal_error;
 use result::opendal_result_list;
+use result::opendal_result_operator_new;
 use types::opendal_blocking_lister;
 
-use crate::error::opendal_code;
 use crate::result::opendal_result_is_exist;
 use crate::result::opendal_result_read;
 use crate::result::opendal_result_stat;
@@ -57,10 +58,9 @@ use crate::types::opendal_operator_ptr;
 /// @param options the pointer to the options for this operators, it could be 
NULL, which means no
 /// option is set
 /// @see opendal_operator_options
-/// @return A valid opendal_operator_ptr setup with the `scheme` and `options` 
is the construction
-/// succeeds. A null opendal_operator_ptr if any error happens.
-///
-/// \remark You may use the `ptr` field of opendal_operator_ptr to check if it 
is NULL.
+/// @return A valid opendal_result_operator_new setup with the `scheme` and 
`options` is the construction
+/// succeeds. On success the operator_ptr field is a valid pointer to a newly 
allocated opendal_operator_ptr,
+/// and the error field is NULL. Otherwise, the operator_ptr field is a NULL 
pointer and the error field.
 ///
 /// # Example
 ///
@@ -72,7 +72,8 @@ use crate::types::opendal_operator_ptr;
 /// opendal_operator_options_set(options, "root", "/myroot");
 ///
 /// // Construct the operator based on the options and scheme
-/// const opendal_operator_ptr *ptr = opendal_operator_new("memory", options);
+/// opendal_result_operator_new result = opendal_operator_new("memory", 
options);
+/// opendal_operator_ptr* op = result.operator_ptr;
 ///
 /// // you could free the options right away since the options is not used 
afterwards
 /// opendal_operator_options_free(options);
@@ -82,24 +83,34 @@ use crate::types::opendal_operator_ptr;
 ///
 /// # Safety
 ///
-/// It is **safe** under two cases below
-/// * The memory pointed to by `scheme` contain a valid nul terminator at the 
end of
-///   the string.
-/// * The `scheme` points to NULL, this function simply returns you a null 
opendal_operator_ptr.
+/// The only unsafe case is passing a invalid c string pointer to the `scheme` 
argument.
 #[no_mangle]
 pub unsafe extern "C" fn opendal_operator_new(
     scheme: *const c_char,
     options: *const opendal_operator_options,
-) -> *const opendal_operator_ptr {
+) -> opendal_result_operator_new {
     if scheme.is_null() {
-        return std::ptr::null();
+        let error = opendal_error::manual_error(
+            error::opendal_code::OPENDAL_CONFIG_INVALID,
+            "The scheme given is pointing at NULL".into(),
+        );
+        let result = opendal_result_operator_new {
+            operator_ptr: std::ptr::null_mut(),
+            error: Box::into_raw(Box::new(error)),
+        };
+        return result;
     }
 
     let scheme_str = unsafe { 
std::ffi::CStr::from_ptr(scheme).to_str().unwrap() };
     let scheme = match od::Scheme::from_str(scheme_str) {
         Ok(s) => s,
-        Err(_) => {
-            return std::ptr::null();
+        Err(e) => {
+            let e = opendal_error::from_opendal_error(e);
+            let result = opendal_result_operator_new {
+                operator_ptr: std::ptr::null_mut(),
+                error: Box::into_raw(Box::new(e)),
+            };
+            return result;
         }
     };
 
@@ -112,32 +123,39 @@ pub unsafe extern "C" fn opendal_operator_new(
 
     let op = match od::Operator::via_map(scheme, map) {
         Ok(o) => o.blocking(),
-        Err(_) => {
-            return std::ptr::null();
+        Err(e) => {
+            let e = opendal_error::from_opendal_error(e);
+            let result = opendal_result_operator_new {
+                operator_ptr: std::ptr::null_mut(),
+                error: Box::into_raw(Box::new(e)),
+            };
+            return result;
         }
     };
 
     // this prevents the operator memory from being dropped by the Box
     let op = opendal_operator_ptr::from(Box::into_raw(Box::new(op)));
-
-    Box::into_raw(Box::new(op))
+    opendal_result_operator_new {
+        operator_ptr: Box::into_raw(Box::new(op)),
+        error: std::ptr::null_mut(),
+    }
 }
 
 /// \brief Blockingly write raw bytes to `path`.
 ///
-/// Write the `bytes` into the `path` blockingly by `op_ptr`, returns the 
opendal_code OPENDAL_OK
-/// if succeeds, others otherwise.
+/// Write the `bytes` into the `path` blockingly by `op_ptr`.
+/// Error is NULL if successful, otherwise it contains the error code and 
error message.
 ///
-/// @NOTE It is important to notice that the `bytes` that is passes in will be 
consumed by this
-///       function.
+/// \note It is important to notice that the `bytes` that is passes in will be 
consumed by this
+///       function. Therefore, you should not use the `bytes` after this 
function returns.
 ///
 /// @param ptr The opendal_operator_ptr created previously
 /// @param path The designated path you want to write your bytes in
 /// @param bytes The opendal_byte typed bytes to be written
 /// @see opendal_operator_ptr
 /// @see opendal_bytes
-/// @see opendal_code
-/// @return OPENDAL_OK if succeeds others otherwise
+/// @see opendal_error
+/// @return NULL if succeeds, otherwise it contains the error code and error 
message.
 ///
 /// # Example
 ///
@@ -150,10 +168,10 @@ pub unsafe extern "C" fn opendal_operator_new(
 /// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
 ///
 /// // now you can write!
-/// opendal_code code = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
+/// opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
 ///
 /// // Assert that this succeeds
-/// assert(code == OPENDAL_OK)
+/// assert(err == NULL);
 /// ```
 ///
 /// # Safety
@@ -172,7 +190,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
     ptr: *const opendal_operator_ptr,
     path: *const c_char,
     bytes: opendal_bytes,
-) -> opendal_code {
+) -> *mut opendal_error {
     if path.is_null() {
         panic!("The path given is pointing at NULL");
     }
@@ -180,24 +198,26 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
     let op = (*ptr).as_ref();
     let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
     match op.write(path, bytes) {
-        Ok(_) => opendal_code::OPENDAL_OK,
-        Err(e) => opendal_code::from_opendal_error(e),
+        Ok(_) => std::ptr::null_mut(),
+        Err(e) => {
+            let e = Box::new(opendal_error::from_opendal_error(e));
+            Box::into_raw(e)
+        }
     }
 }
 
 /// \brief Blockingly read the data from `path`.
 ///
-/// Read the data out from `path` blockingly by operator, returns
-/// an opendal_result_read with error code.
+/// Read the data out from `path` blockingly by operator.
 ///
 /// @param ptr The opendal_operator_ptr created previously
 /// @param path The path you want to read the data out
 /// @see opendal_operator_ptr
 /// @see opendal_result_read
-/// @see opendal_code
+/// @see opendal_error
 /// @return Returns opendal_result_read, the `data` field is a pointer to a 
newly allocated
-/// opendal_bytes, the `code` field contains the error code. If the `code` is 
not OPENDAL_OK,
-/// the `data` field points to NULL.
+/// opendal_bytes, the `error` field contains the error. If the `error` is not 
NULL, then
+/// the operation failed and the `data` field is a nullptr.
 ///
 /// \note If the read operation succeeds, the returned opendal_bytes is newly 
allocated on heap.
 /// After your usage of that, please call opendal_bytes_free() to free the 
space.
@@ -209,7 +229,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
 /// // ... you have write "Hello, World!" to path "/testpath"
 ///
 /// opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath");
-/// assert(r.code == OPENDAL_OK);
+/// assert(r.error == NULL);
 ///
 /// opendal_bytes *bytes = r.data;
 /// assert(bytes->len == 13);
@@ -241,26 +261,29 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
             let v = Box::new(opendal_bytes::new(d));
             opendal_result_read {
                 data: Box::into_raw(v),
-                code: opendal_code::OPENDAL_OK,
+                error: std::ptr::null_mut(),
+            }
+        }
+        Err(e) => {
+            let e = Box::new(opendal_error::from_opendal_error(e));
+            opendal_result_read {
+                data: std::ptr::null_mut(),
+                error: Box::into_raw(e),
             }
         }
-        Err(e) => opendal_result_read {
-            data: std::ptr::null_mut(),
-            code: opendal_code::from_opendal_error(e),
-        },
     }
 }
 
 /// \brief Blockingly delete the object in `path`.
 ///
-/// Delete the object in `path` blockingly by `op_ptr`, returns the 
opendal_code OPENDAL_OK
-/// if succeeds, others otherwise
+/// Delete the object in `path` blockingly by `op_ptr`.
+/// Error is NULL if successful, otherwise it contains the error code and 
error message.
 ///
 /// @param ptr The opendal_operator_ptr created previously
 /// @param path The designated path you want to delete
 /// @see opendal_operator_ptr
-/// @see opendal_code
-/// @return OPENDAL_OK if succeeds others otherwise
+/// @see opendal_error
+/// @return NULL if succeeds, otherwise it contains the error code and error 
message.
 ///
 /// # Example
 ///
@@ -271,13 +294,15 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
 /// // prepare your data
 /// char* data = "Hello, World!";
 /// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
-/// opendal_code code = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
+/// opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath", 
bytes);
+///
+/// assert(error == NULL);
 ///
 /// // now you can delete!
-/// opendal_code code = opendal_operator_blocking_delete(ptr, "/testpath");
+/// opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath");
 ///
 /// // Assert that this succeeds
-/// assert(code == OPENDAL_OK)
+/// assert(error == NULL);
 /// ```
 ///
 /// # Safety
@@ -293,7 +318,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
 pub unsafe extern "C" fn opendal_operator_blocking_delete(
     ptr: *const opendal_operator_ptr,
     path: *const c_char,
-) -> opendal_code {
+) -> *mut opendal_error {
     if path.is_null() {
         panic!("The path given is pointing at NULL");
     }
@@ -301,38 +326,39 @@ pub unsafe extern "C" fn opendal_operator_blocking_delete(
     let op = (*ptr).as_ref();
     let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
     match op.delete(path) {
-        Ok(_) => opendal_code::OPENDAL_OK,
-        Err(e) => opendal_code::from_opendal_error(e),
+        Ok(_) => std::ptr::null_mut(),
+        Err(e) => {
+            let e = Box::new(opendal_error::from_opendal_error(e));
+            Box::into_raw(e)
+        }
     }
 }
 
 /// \brief Check whether the path exists.
 ///
 /// If the operation succeeds, no matter the path exists or not,
-/// the error code should be opendal_code::OPENDAL_OK. Otherwise,
-/// the field `is_exist` is filled with false, and the error code
-/// is set correspondingly.
+/// the error should be a nullptr. Otherwise, the field `is_exist`
+/// is filled with false, and the error is set
 ///
 /// @param ptr The opendal_operator_ptr created previously
 /// @param path The path you want to check existence
 /// @see opendal_operator_ptr
 /// @see opendal_result_is_exist
-/// @see opendal_code
+/// @see opendal_error
 /// @return Returns opendal_result_is_exist, the `is_exist` field contains 
whether the path exists.
-/// However, it the operation fails, the `is_exist` will contains false and 
the error code will be
-/// stored in the `code` field.
+/// However, it the operation fails, the `is_exist` will contains false and 
the error will be set.
 ///
 /// # Example
 ///
 /// ```C
 /// // .. you previously wrote some data to path "/mytest/obj"
 /// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/mytest/obj");
-/// assert(e.code == OPENDAL_OK);
+/// assert(e.error == NULL);
 /// assert(e.is_exist);
 ///
 /// // but you previously did **not** write any data to path "/yourtest/obj"
-/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "yourtest/obj");
-/// assert(e.code == OPENDAL_OK);
+/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, 
"/yourtest/obj");
+/// assert(e.error == NULL);
 /// assert(!e.is_exist);
 /// ```
 ///
@@ -359,37 +385,38 @@ pub unsafe extern "C" fn opendal_operator_is_exist(
     match op.is_exist(path) {
         Ok(e) => opendal_result_is_exist {
             is_exist: e,
-            code: opendal_code::OPENDAL_OK,
-        },
-        Err(err) => opendal_result_is_exist {
-            is_exist: false,
-            code: opendal_code::from_opendal_error(err),
+            error: std::ptr::null_mut(),
         },
+        Err(e) => {
+            let e = Box::new(opendal_error::from_opendal_error(e));
+            opendal_result_is_exist {
+                is_exist: false,
+                error: Box::into_raw(e),
+            }
+        }
     }
 }
 
 /// \brief Stat the path, return its metadata.
 ///
-/// If the operation succeeds, the error code should be
-/// OPENDAL_OK. Otherwise, the field `meta` is filled with
-/// a NULL pointer, and the error code is set correspondingly.
+/// Error is NULL if successful, otherwise it contains the error code and 
error message.
 ///
 /// @param ptr The opendal_operator_ptr created previously
 /// @param path The path you want to stat
 /// @see opendal_operator_ptr
 /// @see opendal_result_stat
 /// @see opendal_metadata
-/// @return Returns opendal_result_stat, containing a metadata and a 
opendal_code.
+/// @return Returns opendal_result_stat, containing a metadata and an 
opendal_error.
 /// If the operation succeeds, the `meta` field would holds a valid metadata 
and
-/// the `code` field should hold OPENDAL_OK. Otherwise the metadata will 
contain a
-/// NULL pointer, i.e. invalid, and the `code` will be set correspondingly.
+/// the `error` field should hold nullptr. Otherwise the metadata will contain 
a
+/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly.
 ///
 /// # Example
 ///
 /// ```C
 /// // ... previously you wrote "Hello, World!" to path "/testpath"
 /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
-/// assert(s.code == OPENDAL_OK);
+/// assert(s.error == NULL);
 ///
 /// const opendal_metadata *meta = s.meta;
 ///
@@ -420,12 +447,15 @@ pub unsafe extern "C" fn opendal_operator_stat(
     match op.stat(path) {
         Ok(m) => opendal_result_stat {
             meta: Box::into_raw(Box::new(opendal_metadata::new(m))),
-            code: opendal_code::OPENDAL_OK,
-        },
-        Err(err) => opendal_result_stat {
-            meta: std::ptr::null_mut(),
-            code: opendal_code::from_opendal_error(err),
+            error: std::ptr::null_mut(),
         },
+        Err(e) => {
+            let e = Box::new(opendal_error::from_opendal_error(e));
+            opendal_result_stat {
+                meta: std::ptr::null_mut(),
+                error: Box::into_raw(e),
+            }
+        }
     }
 }
 
@@ -438,7 +468,10 @@ pub unsafe extern "C" fn opendal_operator_stat(
 /// @param ptr The opendal_operator_ptr created previously
 /// @param path The designated path you want to delete
 /// @see opendal_blocking_lister
-/// @return
+/// @return Returns opendal_result_list, containing a lister and an 
opendal_error.
+/// If the operation succeeds, the `lister` field would holds a valid lister 
and
+/// the `error` field should hold nullptr. Otherwise the `lister`` will 
contain a
+/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly.
 ///
 /// # Example
 ///
@@ -447,7 +480,7 @@ pub unsafe extern "C" fn opendal_operator_stat(
 /// // You have written some data into some files path "root/dir1"
 /// // Your opendal_operator_ptr was called ptr
 /// opendal_result_list l = opendal_operator_blocking_list(ptr, "root/dir1");
-/// assert(l.code == OPENDAL_OK);
+/// assert(l.error == ERROR);
 ///
 /// opendal_blocking_lister *lister = l.lister;
 /// opendal_list_entry *entry;
@@ -488,12 +521,15 @@ pub unsafe extern "C" fn opendal_operator_blocking_list(
     match op.lister(path) {
         Ok(lister) => opendal_result_list {
             lister: 
Box::into_raw(Box::new(opendal_blocking_lister::new(lister))),
-            code: opendal_code::OPENDAL_OK,
+            error: std::ptr::null_mut(),
         },
 
-        Err(e) => opendal_result_list {
-            lister: std::ptr::null_mut(),
-            code: opendal_code::from_opendal_error(e),
-        },
+        Err(e) => {
+            let e = Box::new(opendal_error::from_opendal_error(e));
+            opendal_result_list {
+                lister: std::ptr::null_mut(),
+                error: Box::into_raw(e),
+            }
+        }
     }
 }
diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs
index c0d113718..aa95c1190 100644
--- a/bindings/c/src/result.rs
+++ b/bindings/c/src/result.rs
@@ -20,30 +20,47 @@
 //! "opendal_result_opendal_operator_ptr", which is unacceptable. Therefore,
 //! we are defining all Result types here
 
-use crate::error::opendal_code;
+use crate::error::opendal_error;
 use crate::types::opendal_blocking_lister;
 use crate::types::opendal_bytes;
 use crate::types::opendal_metadata;
+use crate::types::opendal_operator_ptr;
+
+/// \brief The result type returned by opendal_operator_new() operation.
+///
+/// If the init logic is successful, the `operator_ptr` field will be set to a 
valid
+/// pointer, and the `error` field will be set to null. If the init logic 
fails, the
+/// `operator_ptr` field will be set to null, and the `error` field will be 
set to a
+/// valid pointer with error code and error message.
+///
+/// @see opendal_operator_new()
+/// @see opendal_operator_ptr
+/// @see opendal_error
+#[repr(C)]
+pub struct opendal_result_operator_new {
+    pub operator_ptr: *mut opendal_operator_ptr,
+    pub error: *mut opendal_error,
+}
 
 /// \brief The result type returned by opendal's read operation.
 ///
 /// The result type of read operation in opendal C binding, it contains
-/// the data that the read operation returns and a error code.
+/// the data that the read operation returns and an NULL error.
 /// If the read operation failed, the `data` fields should be a nullptr
-/// and the error code is **NOT** OPENDAL_OK.
+/// and the error is not NULL.
 #[repr(C)]
 pub struct opendal_result_read {
     /// The byte array with length returned by read operations
     pub data: *mut opendal_bytes,
-    /// The error code, should be OPENDAL_OK if succeeds
-    pub code: opendal_code,
+    /// The error, if ok, it is null
+    pub error: *mut opendal_error,
 }
 
 /// \brief The result type returned by opendal_operator_is_exist().
 ///
 /// The result type for opendal_operator_is_exist(), the field `is_exist`
-/// contains whether the path exists, and the field `code` contains the
-/// corresponding error code.
+/// contains whether the path exists, and the field `error` contains the
+/// corresponding error. If successful, the `error` field is null.
 ///
 /// \note If the opendal_operator_is_exist() fails, the `is_exist` field
 /// will be set to false.
@@ -51,29 +68,32 @@ pub struct opendal_result_read {
 pub struct opendal_result_is_exist {
     /// Whether the path exists
     pub is_exist: bool,
-    /// The error code, should be OPENDAL_OK if succeeds
-    pub code: opendal_code,
+    /// The error, if ok, it is null
+    pub error: *mut opendal_error,
 }
 
 /// \brief The result type returned by opendal_operator_stat().
 ///
 /// The result type for opendal_operator_stat(), the field `meta` contains the 
metadata
-/// of the path, the field `code` represents whether the stat operation is 
successful.
+/// of the path, the field `error` represents whether the stat operation is 
successful.
+/// If successful, the `error` field is null.
 #[repr(C)]
 pub struct opendal_result_stat {
     /// The metadata output of the stat
     pub meta: *mut opendal_metadata,
-    /// The error code, should be OPENDAL_OK if succeeds
-    pub code: opendal_code,
+    /// The error, if ok, it is null
+    pub error: *mut opendal_error,
 }
 
 /// \brief The result type returned by opendal_operator_blocking_list().
 ///
 /// The result type for opendal_operator_blocking_list(), the field `lister` 
contains the lister
-/// of the path, which is an iterator of the objects under the path. the field 
`code` represents
-/// whether the stat operation is successful.
+/// of the path, which is an iterator of the objects under the path. the field 
`error` represents
+/// whether the stat operation is successful. If successful, the `error` field 
is null.
 #[repr(C)]
 pub struct opendal_result_list {
+    /// The lister, used for further listing operations
     pub lister: *mut opendal_blocking_lister,
-    pub code: opendal_code,
+    /// The error, if ok, it is null
+    pub error: *mut opendal_error,
 }
diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs
index 89d02826e..dbc0aa68e 100644
--- a/bindings/c/src/types.rs
+++ b/bindings/c/src/types.rs
@@ -168,7 +168,7 @@ impl opendal_metadata {
     /// ```C
     /// // ... previously you wrote "Hello, World!" to path "/testpath"
     /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
-    /// assert(s.code == OPENDAL_OK);
+    /// assert(s.error == NULL);
     ///
     /// opendal_metadata *meta = s.meta;
     /// assert(opendal_metadata_content_length(meta) == 13);
@@ -186,7 +186,7 @@ impl opendal_metadata {
     /// ```C
     /// // ... previously you wrote "Hello, World!" to path "/testpath"
     /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
-    /// assert(s.code == OPENDAL_OK);
+    /// assert(s.error == NULL);
     ///
     /// opendal_metadata *meta = s.meta;
     /// assert(opendal_metadata_is_file(meta));
@@ -206,7 +206,7 @@ impl opendal_metadata {
     /// ```C
     /// // ... previously you wrote "Hello, World!" to path "/testpath"
     /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
-    /// assert(s.code == OPENDAL_OK);
+    /// assert(s.error == NULL);
     ///
     /// opendal_metadata *meta = s.meta;
     ///
@@ -377,7 +377,7 @@ impl opendal_list_entry {
     ///
     /// Path is relative to operator's root. Only valid in current operator.
     ///
-    /// @NOTE To free the string, you can directly call free()
+    /// \note To free the string, you can directly call free()
     #[no_mangle]
     pub unsafe extern "C" fn opendal_list_entry_path(&self) -> *mut c_char {
         let s = (*self.inner).path();
@@ -391,7 +391,7 @@ impl opendal_list_entry {
     /// If this entry is a dir, `Name` MUST endswith `/`
     /// Otherwise, `Name` MUST NOT endswith `/`.
     ///
-    /// @NOTE To free the string, you can directly call free()
+    /// \note To free the string, you can directly call free()
     #[no_mangle]
     pub unsafe extern "C" fn opendal_list_entry_name(&self) -> *mut c_char {
         let s = (*self.inner).name();
diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp
index c7d797172..76d5047fa 100644
--- a/bindings/c/tests/bdd.cpp
+++ b/bindings/c/tests/bdd.cpp
@@ -41,8 +41,11 @@ protected:
         opendal_operator_options_set(options, "root", "/myroot");
 
         // Given A new OpenDAL Blocking Operator
-        this->p = opendal_operator_new(scheme.c_str(), options);
-        EXPECT_TRUE(this->p->ptr);
+        opendal_result_operator_new result = 
opendal_operator_new(scheme.c_str(), options);
+        EXPECT_TRUE(result.error == nullptr);
+
+        this->p = result.operator_ptr;
+        EXPECT_TRUE(this->p);
 
         opendal_operator_options_free(options);
     }
@@ -58,17 +61,17 @@ TEST_F(OpendalBddTest, FeatureTest)
         .data = (uint8_t*)this->content.c_str(),
         .len = this->content.length(),
     };
-    opendal_code code = opendal_operator_blocking_write(this->p, 
this->path.c_str(), data);
-    EXPECT_EQ(code, OPENDAL_OK);
+    opendal_error* error = opendal_operator_blocking_write(this->p, 
this->path.c_str(), data);
+    EXPECT_EQ(error, nullptr);
 
     // The blocking file "test" should exist
     opendal_result_is_exist e = opendal_operator_is_exist(this->p, 
this->path.c_str());
-    EXPECT_EQ(e.code, OPENDAL_OK);
+    EXPECT_EQ(e.error, nullptr);
     EXPECT_TRUE(e.is_exist);
 
     // The blocking file "test" entry mode must be file
     opendal_result_stat s = opendal_operator_stat(this->p, this->path.c_str());
-    EXPECT_EQ(s.code, OPENDAL_OK);
+    EXPECT_EQ(s.error, nullptr);
     opendal_metadata* meta = s.meta;
     EXPECT_TRUE(opendal_metadata_is_file(meta));
 
@@ -78,22 +81,22 @@ TEST_F(OpendalBddTest, FeatureTest)
 
     // The blocking file "test" must have content "Hello, World!"
     struct opendal_result_read r = opendal_operator_blocking_read(this->p, 
this->path.c_str());
-    EXPECT_EQ(r.code, OPENDAL_OK);
+    EXPECT_EQ(r.error, nullptr);
     EXPECT_EQ(r.data->len, this->content.length());
     for (int i = 0; i < r.data->len; i++) {
         EXPECT_EQ(this->content[i], (char)(r.data->data[i]));
     }
 
     // The blocking file should be deleted
-    code = opendal_operator_blocking_delete(this->p, this->path.c_str());
-    EXPECT_EQ(code, OPENDAL_OK);
+    error = opendal_operator_blocking_delete(this->p, this->path.c_str());
+    EXPECT_EQ(error, nullptr);
     e = opendal_operator_is_exist(this->p, this->path.c_str());
-    EXPECT_EQ(e.code, OPENDAL_OK);
+    EXPECT_EQ(e.error, nullptr);
     EXPECT_FALSE(e.is_exist);
 
     // The deletion operation should be idempotent
-    code = opendal_operator_blocking_delete(this->p, this->path.c_str());
-    EXPECT_EQ(code, OPENDAL_OK);
+    error = opendal_operator_blocking_delete(this->p, this->path.c_str());
+    EXPECT_EQ(error, nullptr);
 
     opendal_bytes_free(r.data);
 }
diff --git a/bindings/c/tests/error_msg.cpp b/bindings/c/tests/error_msg.cpp
new file mode 100644
index 000000000..3d06f4532
--- /dev/null
+++ b/bindings/c/tests/error_msg.cpp
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "gtest/gtest.h"
+extern "C" {
+#include "opendal.h"
+}
+
+class OpendalErrorTest : public ::testing::Test {
+protected:
+    const opendal_operator_ptr* p;
+
+    // set up a brand new operator
+    void SetUp() override
+    {
+        opendal_operator_options* options = opendal_operator_options_new();
+        opendal_operator_options_set(options, "root", "/myroot");
+
+        opendal_result_operator_new result = opendal_operator_new("memory", 
options);
+        EXPECT_TRUE(result.error == nullptr);
+
+        this->p = result.operator_ptr;
+        EXPECT_TRUE(this->p);
+
+        opendal_operator_options_free(options);
+    }
+
+    void TearDown() override { opendal_operator_free(this->p); }
+};
+
+// Test no memory leak of error message
+TEST_F(OpendalErrorTest, ErrorReadTest)
+{
+    // Initialize a operator for "memory" backend, with no options
+    // The read is supposed to fail
+    opendal_result_read r = opendal_operator_blocking_read(this->p, 
"/testpath");
+    ASSERT_NE(r.error, nullptr);
+    ASSERT_EQ(r.error->code, OPENDAL_NOT_FOUND);
+
+    // Lets check the error message out
+    struct opendal_bytes* error_msg = &r.error->message;
+    ASSERT_NE(error_msg->data, nullptr);
+    ASSERT_GT(error_msg->len, 0);
+
+    // the opendal_bytes read is heap allocated, please free it
+    opendal_bytes_free(r.data);
+
+    // free the error
+    opendal_error_free(r.error);
+}
+
+int main(int argc, char** argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp
index b6b44dd44..7b003e6a6 100644
--- a/bindings/c/tests/list.cpp
+++ b/bindings/c/tests/list.cpp
@@ -35,8 +35,11 @@ protected:
         opendal_operator_options* options = opendal_operator_options_new();
         opendal_operator_options_set(options, "root", "/myroot");
 
-        this->p = opendal_operator_new("memory", options);
-        EXPECT_TRUE(this->p->ptr);
+        opendal_result_operator_new result = opendal_operator_new("memory", 
options);
+        EXPECT_TRUE(result.error == nullptr);
+
+        this->p = result.operator_ptr;
+        EXPECT_TRUE(this->p);
 
         opendal_operator_options_free(options);
     }
@@ -63,11 +66,11 @@ TEST_F(OpendalListTest, ListDirTest)
 
     // write must succeed
     EXPECT_EQ(opendal_operator_blocking_write(this->p, path.c_str(), data),
-        OPENDAL_OK);
+        nullptr);
 
     // list must succeed since the write succeeded
     opendal_result_list l = opendal_operator_blocking_list(this->p, (dname + 
"/").c_str());
-    EXPECT_EQ(l.code, OPENDAL_OK);
+    EXPECT_EQ(l.error, nullptr);
 
     opendal_blocking_lister* lister = l.lister;
 
@@ -80,7 +83,7 @@ TEST_F(OpendalListTest, ListDirTest)
 
         // stat must succeed
         opendal_result_stat s = opendal_operator_stat(this->p, de_path);
-        EXPECT_EQ(s.code, OPENDAL_OK);
+        EXPECT_EQ(s.error, nullptr);
 
         if (!strcmp(de_path, path.c_str())) {
             found = true;
@@ -102,7 +105,7 @@ TEST_F(OpendalListTest, ListDirTest)
 
     // delete
     EXPECT_EQ(opendal_operator_blocking_delete(this->p, path.c_str()),
-        OPENDAL_OK);
+        nullptr);
 
     opendal_lister_free(lister);
 }
diff --git a/bindings/go/opendal.go b/bindings/go/opendal.go
index 1494ab170..4677a99dc 100644
--- a/bindings/go/opendal.go
+++ b/bindings/go/opendal.go
@@ -49,9 +49,14 @@ func NewOperator(scheme string, opt Options) (*Operator, 
error) {
        for k, v := range opt {
                C.opendal_operator_options_set(opts, C.CString(k), C.CString(v))
        }
-       op := C.opendal_operator_new(C.CString(scheme), opts)
+       ret := C.opendal_operator_new(C.CString(scheme), opts)
+       if ret.error != nil {
+               defer C.opendal_error_free(ret.error)
+               code, message := parseError(ret.error)
+               return nil, errors.New(fmt.Sprintf("create operator failed, 
error code: %d, error message: %s", code, message))
+       }
        return &Operator{
-               inner: op,
+               inner: ret.operator_ptr,
        }, nil
 }
 
@@ -61,22 +66,36 @@ func (o *Operator) Write(key string, value []byte) error {
        }
        bytes := C.opendal_bytes{data: (*C.uchar)(unsafe.Pointer(&value[0])), 
len: C.ulong(len(value))}
        ret := C.opendal_operator_blocking_write(o.inner, C.CString(key), bytes)
-       if ret != 0 {
-               return errors.New(fmt.Sprintf("write failed, error code: %d", 
ret))
+       if ret != nil {
+               defer C.opendal_error_free(ret)
+               code, message := parseError(ret)
+               return errors.New(fmt.Sprintf("write failed, error code: %d, 
error message: %s", code, message))
        }
        return nil
 }
 
 func (o *Operator) Read(key string) ([]byte, error) {
-       result := C.opendal_operator_blocking_read(o.inner, C.CString(key))
-       ret := int(result.code)
-       if ret != 0 {
-               return nil, errors.New(fmt.Sprintf("write failed, error code: 
%d", ret))
+       ret := C.opendal_operator_blocking_read(o.inner, C.CString(key))
+       if ret.error != nil {
+               defer C.opendal_error_free(ret.error)
+               code, message := parseError(ret.error)
+               return nil, errors.New(fmt.Sprintf("read failed, error code: 
%d, error message: %s", code, message))
        }
-       return C.GoBytes(unsafe.Pointer(result.data.data), 
C.int(result.data.len)), nil
+       return C.GoBytes(unsafe.Pointer(ret.data.data), C.int(ret.data.len)), 
nil
 }
 
 func (o *Operator) Close() error {
        C.opendal_operator_free(o.inner)
        return nil
 }
+
+func decodeBytes(bs C.opendal_bytes) []byte {
+       return C.GoBytes(unsafe.Pointer(bs.data), C.int(bs.len))
+}
+
+func parseError(err *C.opendal_error) (int, string) {
+       code := int(err.code)
+       message := string(decodeBytes(err.message))
+
+       return code, message
+}
diff --git a/bindings/go/opendal_test.go b/bindings/go/opendal_test.go
index 6723b67e2..9ac76c273 100644
--- a/bindings/go/opendal_test.go
+++ b/bindings/go/opendal_test.go
@@ -38,7 +38,7 @@ func TestOperations(t *testing.T) {
        assert.NoError(t, err)
        assert.Equal(t, "Hello World from OpenDAL CGO!", string(value))
 
-    println(string(value))
+       println(string(value))
 }
 
 func BenchmarkOperator_Write(b *testing.B) {
@@ -50,7 +50,7 @@ func BenchmarkOperator_Write(b *testing.B) {
        bs := bytes.Repeat([]byte("a"), 1024*1024)
        op.Write("test", bs)
 
-    b.SetBytes(1024*1024)
+       b.SetBytes(1024 * 1024)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                op.Read("test")
diff --git a/bindings/swift/.swift-format b/bindings/swift/.swift-format
new file mode 100644
index 000000000..d5d1e0158
--- /dev/null
+++ b/bindings/swift/.swift-format
@@ -0,0 +1,11 @@
+{
+    "version": 1,
+    "lineLength": 100,
+    "indentation": {
+        "spaces": 4
+    },
+    "maximumBlankLines": 1,
+    "respectsExistingLineBreaks": true,
+    "lineBreakBeforeControlFlowKeywords": true,
+    "lineBreakBeforeEachArgument": true
+}
diff --git a/bindings/swift/OpenDAL/Package.swift 
b/bindings/swift/OpenDAL/Package.swift
index d31439f32..f861d7697 100644
--- a/bindings/swift/OpenDAL/Package.swift
+++ b/bindings/swift/OpenDAL/Package.swift
@@ -27,7 +27,8 @@ let package = Package(
     products: [
         .library(
             name: "OpenDAL",
-            targets: ["OpenDAL"]),
+            targets: ["OpenDAL"]
+        )
     ],
     targets: [
         .systemLibrary(name: "COpenDAL"),
@@ -35,10 +36,12 @@ let package = Package(
             name: "OpenDAL",
             dependencies: ["COpenDAL"],
             linkerSettings: [
-                .unsafeFlags(["-L\(packageRoot)/Sources/COpenDAL/lib"]),
-            ]),
+                .unsafeFlags(["-L\(packageRoot)/Sources/COpenDAL/lib"])
+            ]
+        ),
         .testTarget(
             name: "OpenDALTests",
-            dependencies: ["OpenDAL"]),
+            dependencies: ["OpenDAL"]
+        ),
     ]
 )
diff --git a/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift 
b/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift
index 92d0c8270..8e6193375 100644
--- a/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift
+++ b/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift
@@ -15,8 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
-import Foundation
 import COpenDAL
+import Foundation
 
 extension Data {
     /// Creates a new data by managing `opendal_bytes` as its
@@ -25,15 +25,15 @@ extension Data {
     /// This can be used to read data from Rust with zero-copying.
     /// The underlying buffer will be freed when the data gets
     /// deallocated.
-    init?(openDALBytes: UnsafeMutablePointer<opendal_bytes>) {
-        guard let address = UnsafeRawPointer(openDALBytes.pointee.data) else {
-            return nil
-        }
+    init(openDALBytes: UnsafeMutablePointer<opendal_bytes>) {
+        let address = UnsafeRawPointer(openDALBytes.pointee.data)!
         let length = Int(openDALBytes.pointee.len)
-        self.init(bytesNoCopy: .init(mutating: address),
-                  count: length,
-                  deallocator: .custom({ _, _ in
-            opendal_bytes_free(openDALBytes)
-        }))
+        self.init(
+            bytesNoCopy: .init(mutating: address),
+            count: length,
+            deallocator: .custom({ _, _ in
+                opendal_bytes_free(openDALBytes)
+            })
+        )
     }
 }
diff --git a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift 
b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift
index 1f2f91d1a..6104c17c1 100644
--- a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift
+++ b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift
@@ -15,75 +15,84 @@
 // specific language governing permissions and limitations
 // under the License.
 
-import Foundation
 import COpenDAL
+import Foundation
 
-public enum OperatorError: Error {
-    case failedToBuild
-    case operationError(UInt32)
+public struct OperatorError: Error {
+    let code: UInt32
+    let message: Data
 }
 
-/// A type used to access almost all OpenDAL APIs.
 public class Operator {
     var nativeOp: UnsafePointer<opendal_operator_ptr>
-    
+
     deinit {
         opendal_operator_free(nativeOp)
     }
-    
-    /// Creates an operator with the given options.
-    ///
-    /// - Parameter options: The option map for creating the operator.
-    /// - Throws: `OperatorError` value that indicates an error if failed.
-    public init(scheme: String, options: [String : String] = [:]) throws {
+
+    public init(scheme: String, options: [String: String] = [:]) throws {
         let nativeOptions = opendal_operator_options_new()
         defer {
             opendal_operator_options_free(nativeOptions)
         }
-            
+
         for option in options {
             opendal_operator_options_set(nativeOptions, option.key, 
option.value)
         }
-        
-        guard let nativeOp = opendal_operator_new(scheme, nativeOptions) else {
-            throw OperatorError.failedToBuild
-        }
-        
-        guard nativeOp.pointee.ptr != nil else {
-            throw OperatorError.failedToBuild
+
+        let ret = opendal_operator_new(scheme, nativeOptions)
+        if let err = ret.error {
+            defer {
+                opendal_error_free(err)
+            }
+            let immutableErr = err.pointee
+            let messagePointer = withUnsafePointer(to: immutableErr.message) { 
$0 }
+            let messageLength = Int(immutableErr.message.len)
+            throw OperatorError(
+                code: immutableErr.code.rawValue,
+                message: Data(bytes: messagePointer, count: messageLength)
+            )
         }
 
-        self.nativeOp = nativeOp
+        self.nativeOp = UnsafePointer(ret.operator_ptr)!
     }
-    
-    /// Blockingly write the data to a given path.
-    ///
-    /// - Parameter data: The content to be written.
-    /// - Parameter path: The destination path for writing the data.
-    /// - Throws: `OperatorError` value that indicates an error if failed to 
write.
+
     public func blockingWrite(_ data: Data, to path: String) throws {
-        let code = data.withUnsafeBytes { dataPointer in
+        let ret = data.withUnsafeBytes { dataPointer in
             let address = dataPointer.baseAddress!.assumingMemoryBound(to: 
UInt8.self)
             let bytes = opendal_bytes(data: address, len: 
UInt(dataPointer.count))
             return opendal_operator_blocking_write(nativeOp, path, bytes)
         }
-        
-        guard code == OPENDAL_OK else {
-            throw OperatorError.operationError(code.rawValue)
+
+        if let err = ret {
+            defer {
+                opendal_error_free(err)
+            }
+            let immutableErr = err.pointee
+            let messagePointer = withUnsafePointer(to: immutableErr.message) { 
$0 }
+            let messageLength = Int(immutableErr.message.len)
+            throw OperatorError(
+                code: immutableErr.code.rawValue,
+                message: Data(bytes: messagePointer, count: messageLength)
+            )
         }
     }
-    
-    /// Blockingly read the data from a given path.
-    ///
-    /// - Parameter path: Path of the data to read.
-    /// - Returns: `NativeData` object if the data exists.
-    /// - Throws: `OperatorError` value that indicates an error if failed to 
read.
-    public func blockingRead(_ path: String) throws -> Data? {
-        let result = opendal_operator_blocking_read(nativeOp, path)
-        guard result.code == OPENDAL_OK else {
-            throw OperatorError.operationError(result.code.rawValue)
+
+    public func blockingRead(_ path: String) throws -> Data {
+        let ret = opendal_operator_blocking_read(nativeOp, path)
+        if let err = ret.error {
+            defer {
+                opendal_error_free(err)
+            }
+            let immutableErr = err.pointee
+            let messagePointer = withUnsafePointer(to: immutableErr.message) { 
$0 }
+            let messageLength = Int(immutableErr.message.len)
+            throw OperatorError(
+                code: immutableErr.code.rawValue,
+                message: Data(bytes: messagePointer, count: messageLength)
+            )
         }
-        
-        return .init(openDALBytes: result.data)
+
+        return Data(openDALBytes: ret.data)
     }
 }
diff --git a/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift 
b/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift
index a776efdbd..ef17c7887 100644
--- a/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift
+++ b/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift
@@ -16,26 +16,27 @@
 // under the License.
 
 import XCTest
+
 @testable import OpenDAL
 
 final class OpenDALTests: XCTestCase {
     func testSimple() throws {
-        let op = try Operator(scheme: "memory", options: [
-            "root": "/myroot"
-        ])
-        
+        let op = try Operator(
+            scheme: "memory",
+            options: [
+                "root": "/myroot"
+            ]
+        )
+
         let testContents = Data([1, 2, 3, 4])
         try op.blockingWrite(testContents, to: "test")
-        
-        guard let readContents = try op.blockingRead("test") else {
-            XCTFail("Expected a `Data`")
-            return
-        }
-        
+
+        let readContents = try op.blockingRead("test")
+
         for (testByte, readByte) in zip(testContents, readContents) {
             XCTAssertEqual(testByte, readByte)
         }
-        
+
         XCTAssertThrowsError(try op.blockingRead("test_not_exists"))
     }
 }
diff --git a/bindings/zig/README.md b/bindings/zig/README.md
index 7f25eb470..315c686cb 100644
--- a/bindings/zig/README.md
+++ b/bindings/zig/README.md
@@ -16,7 +16,7 @@ To compile OpenDAL from source code, you need:
 # build libopendal_c (underneath call make -C ../c)
 zig build libopendal_c
 # build and run unit tests
-zig build test -fsummary
+zig build test --summary all
 ```
 
 ## License
diff --git a/bindings/zig/src/opendal.zig b/bindings/zig/src/opendal.zig
index 93c504742..02bfc41e9 100644
--- a/bindings/zig/src/opendal.zig
+++ b/bindings/zig/src/opendal.zig
@@ -19,8 +19,6 @@ pub const c = @cImport(@cInclude("opendal.h"));
 
 // Zig code get values C code
 pub const Code = enum(c.opendal_code) {
-    OK = c.OPENDAL_OK,
-    ERROR = c.OPENDAL_ERROR,
     UNEXPECTED = c.OPENDAL_UNEXPECTED,
     UNSUPPORTED = c.OPENDAL_UNSUPPORTED,
     CONFIG_INVALID = c.OPENDAL_CONFIG_INVALID,
@@ -58,7 +56,6 @@ pub fn codeToError(code: c.opendal_code) 
OpendalError!c.opendal_code {
         c.OPENDAL_ALREADY_EXISTS => error.AlreadyExists,
         c.OPENDAL_RATE_LIMITED => error.RateLimited,
         c.OPENDAL_IS_SAME_FILE => error.IsSameFile,
-        else => c.OPENDAL_ERROR,
     };
 }
 pub fn errorToCode(err: OpendalError) c_int {
diff --git a/bindings/zig/test/bdd.zig b/bindings/zig/test/bdd.zig
index 79748fa3b..c835e45b3 100644
--- a/bindings/zig/test/bdd.zig
+++ b/bindings/zig/test/bdd.zig
@@ -40,7 +40,9 @@ test "Opendal BDD test" {
             opendal.c.opendal_operator_options_set(options, "root", "/myroot");
 
             // Given A new OpenDAL Blocking Operator
-            self.p = opendal.c.opendal_operator_new(self.scheme, options);
+            var result = opendal.c.opendal_operator_new(self.scheme, options);
+            testing.expectEqual(result.@"error", null) catch unreachable;
+            self.p = result.operator_ptr;
 
             return self;
         }
@@ -61,17 +63,17 @@ test "Opendal BDD test" {
         // c_str does not have len field (.* is ptr)
         .len = std.mem.len(testkit.content),
     };
-    const code = opendal.c.opendal_operator_blocking_write(testkit.p, 
testkit.path, data);
-    try testing.expectEqual(code, @intFromEnum(Code.OK));
+    const result = opendal.c.opendal_operator_blocking_write(testkit.p, 
testkit.path, data);
+    try testing.expectEqual(result, null);
 
     // The blocking file "test" should exist
     var e: opendal.c.opendal_result_is_exist = 
opendal.c.opendal_operator_is_exist(testkit.p, testkit.path);
-    try testing.expectEqual(e.code, @intFromEnum(Code.OK));
+    try testing.expectEqual(e.@"error", null);
     try testing.expect(e.is_exist);
 
     // The blocking file "test" entry mode must be file
     var s: opendal.c.opendal_result_stat = 
opendal.c.opendal_operator_stat(testkit.p, testkit.path);
-    try testing.expectEqual(s.code, @intFromEnum(Code.OK));
+    try testing.expectEqual(s.@"error", null);
     var meta: [*c]opendal.c.opendal_metadata = s.meta;
     try testing.expect(opendal.c.opendal_metadata_is_file(meta));
 
@@ -82,7 +84,7 @@ test "Opendal BDD test" {
     // The blocking file "test" must have content "Hello, World!"
     var r: opendal.c.opendal_result_read = 
opendal.c.opendal_operator_blocking_read(testkit.p, testkit.path);
     defer opendal.c.opendal_bytes_free(r.data);
-    try testing.expect(r.code == @intFromEnum(Code.OK));
+    try testing.expect(r.@"error" == null);
     try testing.expectEqual(std.mem.len(testkit.content), r.data.*.len);
 
     var count: usize = 0;

Reply via email to