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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

4回答

1695閲覧

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

catindog

総合スコア16

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2017/07/25 08:05

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

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

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

rust

1use std::os::raw::{c_char}; 2use std::ffi::CString; 3 4#[no_mangle] 5pub extern "C" fn echo_rust_i(x:i32) { 6 println!("Hello Rust {}", x); 7} 8 9#[no_mangle] 10pub extern "C" fn echo_rust_string(x: *mut c_char) { 11 let x = unsafe { CString::from_raw(x).into_string().unwrap() }; 12 println!("Hello Rust {}", x ); 13} 14 15#[no_mangle] 16pub extern "C" fn rust_multiply(x:i32, y:i32) -> i32 { 17 x*y 18} 19 20fn main() { 21 println!("hello world"); 22 echo_rust_i(123); 23 echo_rust_string( HOGE("World") ); 24}

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

console

1$ rustc -V 2rustc 1.15.1

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

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

まず、タイトルにある

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に解放を任せてしまえば安全なので以下のようなコードになるでしょうか。

rust

1use std::os::raw::{c_char}; 2use std::ffi::{CString, CStr}; 3 4 5#[no_mangle] 6pub extern "C" fn echo_rust_string(x: *const c_char) { 7 // ここでは文字列を読むだけなので`*const`で十分ですし、`CStr`で十分です。 8 let x = unsafe { CStr::from_ptr(x).to_str() }; 9 let x = x.unwrap(); 10 println!("Hello Rust {}", x ); 11} 12 13#[no_mangle] 14pub extern "C" fn free_rust_string(x: *mut c_char) { 15 // ここでライフタイムが終わるので文字列が解放される 16 // この文字列は **必ず** `CString`で確保した文字列でないといけない 17 unsafe { CString::from_raw(x) } 18} 19 20 21 22fn main() { 23 println!("hello world"); 24 // ここでC向けに文字列を確保する 25 let xin = CString::new( "world"); 26 let xin = xin.into_raw(); 27 echo_rust_string( xin ); 28 free_rust_string(xin); 29}

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

rust

1use std::os::raw::{c_char}; 2use std::ffi::{CString, CStr}; 3 4 5#[no_mangle] 6pub extern "C" fn echo_rust_string(x: *const c_char) { 7 // ここでは文字列を読むだけなので`*const`で十分ですし、`CStr`で十分です。 8 let x = unsafe { CStr::from_ptr(x).to_str() }; 9 let x = x.unwrap(); 10 println!("Hello Rust {}", x ); 11}

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

go

1package main 2 3/* 4#cgo LDFLAGS: -ldl ./libsample.so ./libsample_rust.so 5#include <stdlib.h> 6#include <dlfcn.h> 7#include "bridge.h" 8*/ 9import "C" 10import "unsafe" 11 12func main() { 13 s := C.CString("ねむすぎる") 14 C.echo_rust_string(s) 15 C.free(unsafe.Pointer(s)) 16}

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

投稿2017/07/27 02:05

blackenedgold

総合スコア468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

catindog

2017/07/27 03:54

いずれの回答も助かりました。 GoとRustのプリミティブ型のバインディングをやりたかったのですが、Cの経験が乏しく、ポインタとしてどのように適切に鳥回せばいいのかわかりませんでした。 メモリ解放などは気づいていなかったので、大変参考になりました
guest

0

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

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

rust

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

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

投稿2017/07/25 16:44

ubnt-intrepid

総合スコア58

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

catindog

2017/07/25 16:48

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

0

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

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

rust

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

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

rust

1let x_ptr = CString::new("world").unwrap().as_ptr(); 2echo_rust_string(x_ptr); // 呼び出す前に一時オブジェクトが破棄されるためポインタの指す値は無効

rust

1#[no_mangle] 2pub extern "C" fn hoge() -> *const c_char { 3 let s = CString::new("hoge").unwrap(); 4 s.as_ptr() 5 // スコープを抜けるときに s が解放されるためポインタは無効 6}

投稿2017/07/25 17:22

編集2017/07/25 17:26
ubnt-intrepid

総合スコア58

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

catindog

2017/07/26 03:41

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

0

自己解決

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

rust

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

投稿2017/07/25 08:34

catindog

総合スコア16

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問