質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.60%

Rustでリテラルの文字を*mut c_char型する方法

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 2,454

catindog

score 16

Goでimport "C"を利用してRustで作成したコードを利用したいと考えています  

数値や文字(C.String型)で渡してRustで処理するのはうまくったのですが、Rust内部で記述したリテラルの文字列を*mut c_char型に変換する方法がわかりません  

具体的には以下のようなことをしたいと思いますが、hogeの処理がわかりません

use std::os::raw::{c_char};
use std::ffi::CString;

#[no_mangle]
pub extern "C" fn echo_rust_i(x:i32) {
  println!("Hello Rust {}", x);
}

#[no_mangle]
pub extern "C" fn echo_rust_string(x: *mut c_char) {
  let x = unsafe { CString::from_raw(x).into_string().unwrap() };
  println!("Hello Rust {}", x );
}

#[no_mangle]
pub extern "C" fn rust_multiply(x:i32, y:i32) -> i32 {
  x*y
}

fn main() {
  println!("hello world");
  echo_rust_i(123);
  echo_rust_string( HOGE("World") );
}

rustcのバージョンはこれです

$ rustc -V
rustc 1.15.1

Githubにコード全体はあります

教えていただけると幸いです

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+3

自己解決されたようですがちょっと気になったのでコメントします。

まず、タイトルにある

Rustでリテラルの文字を*mut c_char型する方法

はありません。リテラルの文字列はリードオンリーな領域に確保されるので*mutには変換できません。無理矢理やれば「型上は」可能ですが、実際に操作しようとするとunsafeの中で違反を起こすことになります。

解決したとおっしゃってる方法(concat!( "world", "\0"))は一旦(&'static strではなくて)Stringに変換しているので先程よりはマシですがこれもmutでない変数を*mutに変換しているので問題あります。

Cへ渡す文字(列)へのポインタが欲しいならstd::ffiにあるCStr/CStringを使います。また、今回*mutを使われてますが、書き込みをしないなら*constを使います。*const u8ならリテラルの文字列からでもas_ptrが使えるのであとは自己責任でu8c_charが等しいことを保障すれば欲しい値が手に入るでしょう。

さて、コメントを見るにRustで文字列を確保してGoで利用したいというのがモチベーションのようですが、Rustは所有権でメモリ管理をするので多少工夫しないとdangling pointerかメモリリークかのどちらかを引き起こしてしまいます。最終的にRustに解放を任せてしまえば安全なので以下のようなコードになるでしょうか。

use std::os::raw::{c_char};
use std::ffi::{CString, CStr};


#[no_mangle]
pub extern "C" fn echo_rust_string(x: *const c_char) {
  // ここでは文字列を読むだけなので`*const`で十分ですし、`CStr`で十分です。
  let x = unsafe { CStr::from_ptr(x).to_str() };
  let x = x.unwrap();
  println!("Hello Rust {}", x );
}

#[no_mangle]
pub extern "C" fn free_rust_string(x: *mut c_char) {
    // ここでライフタイムが終わるので文字列が解放される
    // この文字列は **必ず** `CString`で確保した文字列でないといけない
    unsafe { CString::from_raw(x) }
}



fn main() {
  println!("hello world");
  // ここでC向けに文字列を確保する
  let xin = CString::new( "world");
  let xin = xin.into_raw();
  echo_rust_string( xin );
  free_rust_string(xin);
}

よく分からないのですが、コメントとここに載せてあるコード上ではこれで良いのですが、リンクを貼ってあるgithubレポジトリ上ではGoで確保したメモリをRustに渡しておられるようなのでそちらであればまた回答は変わります。
Goで確保したメモリならRustで管理はしないので文字列の生成と解放が不要で、このようなコードになるでしょう。

use std::os::raw::{c_char};
use std::ffi::{CString, CStr};


#[no_mangle]
pub extern "C" fn echo_rust_string(x: *const c_char) {
  // ここでは文字列を読むだけなので`*const`で十分ですし、`CStr`で十分です。
  let x = unsafe { CStr::from_ptr(x).to_str() };
  let x = x.unwrap();
  println!("Hello Rust {}", x );
}

そしてgoで確保した文字列はGoで面倒を看ることになります

package main

/*
#cgo LDFLAGS: -ldl ./libsample.so ./libsample_rust.so
#include <stdlib.h>
#include <dlfcn.h>
#include "bridge.h"
*/
import "C"
import "unsafe"

func main() {
    s := C.CString("ねむすぎる")
    C.echo_rust_string(s)
    C.free(unsafe.Pointer(s))
}

今回やりたいことの意図が汲み取れなかったので複数の回答を用意しましたがどれかが参考になれば幸いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/27 12:54

    いずれの回答も助かりました。

    GoとRustのプリミティブ型のバインディングをやりたかったのですが、Cの経験が乏しく、ポインタとしてどのように適切に鳥回せばいいのかわかりませんでした。

    メモリ解放などは気づいていなかったので、大変参考になりました

    キャンセル

+2

CString::from_raw(ptr) は *mut c_char から文字列を得る用途としては基本的には推奨されていません。このコンストラクタの引数として渡すことのできるポインタは CString::into_raw(self) の戻り値として返されるものに限定され、それ以外のポインタを渡した場合は未定義動作となります(参考)。

C側の文字列をRustのStringに変換するには CStr::from_ptr を使用してください:

#[no_mangle]
pub extern "C" fn echo_rust_string(x_ptr: *const c_char) {
    let x = unsafe { CStr::from_ptr(x_ptr) };
    if let Ok(x) = x.to_str() {
        println!("Hello Rust {}", x);
    }
}

余談ですが、FFI で外に出している関数内でパニックすると未定義動作を起こす可能性があるため、安易に unwrap() しないよう注意してください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/26 01:48

    ありがとうございます。

    言語詳細まで手が回っておらず、ここまで深いレベルの回答をいただけて大変ためになります

    キャンセル

check解決した方法

0

すみません、丁寧にやっていったら自己解決しました

  let xin = concat!( "world", "\0");                                                                                                                                                                 
  let xin = xin.as_ptr() as *mut c_char;                                                                                                                                                             
  echo_rust_string( xinet);  

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

質問に対する回答になっていなかったので補足します。

質問の意味は「Rust 側で定義した文字列を *mut c_char として C/Go 側に返す方法」と解釈しました。これは CString::as_ptr() を使用します。

let x = CString::new("world").unwrap();
echo_rust_string(x.as_ptr());

当然ですが、 x 自身の生存範囲はプログラマ自身が責任を持って管理する必要があります。例えば、次のような例は無効です。

let x_ptr = CString::new("world").unwrap().as_ptr();
echo_rust_string(x_ptr); // 呼び出す前に一時オブジェクトが破棄されるためポインタの指す値は無効
#[no_mangle]
pub extern "C" fn hoge() -> *const c_char {
    let s = CString::new("hoge").unwrap();
    s.as_ptr()
    // スコープを抜けるときに s が解放されるためポインタは無効
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/26 12:41

    ありがとうございます。

    明示的にmallocして関数を抜けてもメモリが保持されている必要があり、Rustでのメモリの保存ん仕方がわからなかったので、助かりました

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.60%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る