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

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

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

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

Q&A

解決済

4回答

1535閲覧

Rust内でC言語のprintfを実現したい。

退会済みユーザー

退会済みユーザー

総合スコア0

Rust

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

0グッド

1クリップ

投稿2020/06/28 07:49

編集2020/06/28 08:06

###Rust言語上で、C言語のprintfを実現したい。

Windows上でRustをコンパイルする際に、
Rust言語上で、printfを使いたくて以下のようにコードを記述していますが、

error: linking with `link.exe` failed: exit code: 1120

というエラーがでて、コンパイルできません。

printf関数は下記のように定義しております。

printfのシグネチャは
https://docs.rs/printf/0.1.0/printf/
上記より参照しています。

Rust

1pub fn printf_c_string(output : Vec<u8>) -> isize { 2 3 unsafe { 4 extern "C" { 5 fn printf(format: *const c_char, args: *mut c_void) -> String; 6 } 7 8 let c_percent = (CString::new("%s".to_string()).unwrap().as_ptr()) as *const c_char; 9 10 let c_string = (CString::new(output).unwrap().as_ptr()) as *mut c_void; 11 12 printf(c_percent, c_string); 13 } 14 return -1; 15} 16 17 18fn main () 19{ 20 21 let v : Vec<u8> = Vec::new(); 22 printf_c_string(v); 23 24} 25

ちなみに、
cargo.tomlファイルは下記のように定義しています。

toml

1[package] 2name = "rust_ishell" 3version = "0.1.0" 4 5edition = "2018" 6 7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 9[dependencies] 10ctrlc = "*" 11tempfile = "*" 12tempdir = "*" 13cstr_core = "*" 14libc = "*" 15printf = "*" 16printf-rs = "*" 17 18[profile.release] 19opt-level = 3

ただ、Rustのマニュアルにある、fputs関数は、下記のように記述したところ
動作しています。

Rust

1 2pub 3fn print_c_string(output :Vec<u8>) -> isize { 4 unsafe { 5 extern "C" { 6 fn puts(s: *const c_char) -> c_int; 7 } 8 // Vectorのサイズを取得 9 let output_size: isize = output.len() as isize; 10 11 // VectorからCStringを生成 12 let to_print = CString::new(output); 13 // check_type(&to_print); 14 15 // 無事にCStringを取り出せたとき 16 if (to_print.is_ok() == true) { 17 puts(to_print.unwrap().as_ptr()); 18 return output_size; 19 } else { 20 panic!("{}", to_print.unwrap_err()) 21 } 22 return -1; 23 } 24} 25 26fn main() 27{ 28 let v = Vec::new(); 29 print_c_string(v); 30}

なぜこのように、Cのprintfに固執する理由は、
RustでUTF-8以外の文字列を読み込みたいのです。

上記の Cのfputs関数ですと、

Rust

1let b = fs::File::open(path).unwrap().bytes();

などでbyte単位で読み出して、Vec<u8>としてとりだして、
as_ptr()で puts関数に渡してやると、Shift_jisのままプロンプトで表記ができるのです。

どんなたか、Rust言語上でCのprintfを実現するための方法をご存知でしたらご教授くださいませ。

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

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

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

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

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

guest

回答4

0

ベストアンサー

RustでShift_JISを扱う場合、すでに回答のある通りencoding_rsを使うのがいいと思いますが、
元々のエラーについてもご説明します。

Windows(おそらくMSVCをお使いだと思います)では、printfstdio.hにインラインで定義されるように
なっているので、リンク時に参照するライブラリには存在せず、それが冒頭のリンクエラーになります。
ただし、昔のMSVCではライブラリに存在したらしく、そのころとの互換性のために現在でもlegacy_stdio_definitions.libというライブラリでprintfが提供されています。
従ってこれをRustからリンクすることで、printfを呼ぶことができます。

手元のWindows環境で動作したコードは以下の通りで、修正点は3か所です。

  • printfの戻り値を修正(String -> c_int
  • #[link(name="legacy_stdio_definitions", kind="static")]を追加
  • CStringのライフタイム(moshさんがご指摘の件)

rust

1use std::ffi::CString; 2use std::os::raw::{c_char, c_int, c_void}; 3 4pub fn printf_c_string(output: Vec<u8>) -> isize { 5 unsafe { 6 #[link(name="legacy_stdio_definitions", kind="static")] 7 extern "C" { 8 fn printf(format: *const c_char, args: *mut c_void) -> c_int; 9 } 10 11 let c_percent = CString::new("%s".to_string()).unwrap(); 12 let c_percent_ptr = c_percent.as_ptr() as *const c_char; 13 14 let c_string = CString::new(output).unwrap(); 15 let c_string_ptr = c_string.as_ptr() as *mut c_void; 16 17 printf(c_percent_ptr, c_string_ptr); 18 } 19 return -1; 20} 21 22fn main() { 23 let mut v: Vec<u8> = Vec::new(); 24 v.push('a' as u8); 25 v.push('a' as u8); 26 printf_c_string(v); 27}

投稿2020/06/29 02:42

dalance

総合スコア86

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

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

0

RustでUTF-8以外の文字列を読み込みたいのです。
Shift_jisのままプロンプトで表記ができるのです。

そのためだけだとしたら、encoding_rsを使えばいいと思います。

rust

1use std::io::{self, Write}; 2use encoding_rs::*; 3 4fn main() -> io::Result<()> { 5 // shift_jis を読み込む 6 let path = "/path/to/file"; 7 let b: Vec<u8> = fs::File::open(path).read()?; 8 let (cow, _encoding_used_in, _had_errors_in) = SHIFT_JIS.decode(b); 9 let s: String = cow.into_owned(); 10 11 // sにしたいことをする 12 13 // shift_jis にエンコードして出力する 14 let (cow_out, _encoding_used_out, _had_errors_out) = SHIFT_JIS.encode(s); 15 let stdout = io::stdout(); 16 let mut handle = stdout.lock(); 17 handle.write_all(cow_out)?; 18}

投稿2020/06/28 09:41

編集2020/06/28 09:42
YufanLou

総合スコア464

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

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

0

そのcrateにはvoid*をva_listへ変換するC言語のラッパーコードが含まれていて、Rust側ではそのラッパを呼ぶことでprintfを呼んでいます。したがって、そのcrateのprintfのシグネチャはそもそも本来のprintfとは異なるものです。
可変長引数を渡したいなら、...を使うことで可能になります。

rust

1extern "C" { 2 fn printf(format: *const libc::c_char, ...) -> libc::c_int; 3} 4 5fn main(){ 6 let i: libc::c_int = 42; 7 let j: libc::c_double = 3.14; 8 let fmt = std::ffi::CString::new("%d %f\n").unwrap(); 9 10 unsafe{ 11 printf(fmt.as_c_str().as_ptr(), i, j); 12 } 13}

似たような質問をRustのフォーラムで見つけたので貼っておきます。
How to wrap printf in libc properly?

投稿2020/06/28 08:49

編集2020/06/28 08:56
equal-l2

総合スコア172

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

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

0

質問の回答ではないのですが、コードにuse after freeがあるので指摘しておきます。

as_ptrのドキュメントのWarningのコード例と同じことをしてしまっています。
CStringは変数にバインドしていないのでas_ptrを呼び出すところまでしか生存しません。つまりポインタは即座に無効になっています。
以下のようにすれば直ります。

rust

1let c_percent_buf = CString::new("%s".to_string()).unwrap(); 2let c_percent = c_percent_buf.as_ptr() as *const c_char; 3 4let c_string = CString::new(output).unwrap(); 5let c_str = c_string.as_ptr() as *mut c_void; 6 7printf(c_percent, c_str);

投稿2020/06/28 14:31

mosh

総合スコア18

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問